powerpc/4xx/ocm: Fix phys_addr_t printf warnings
[linux-2.6-microblaze.git] / arch / powerpc / perf / 8xx-pmu.c
1 /*
2  * Performance event support - PPC 8xx
3  *
4  * Copyright 2016 Christophe Leroy, CS Systemes d'Information
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/perf_event.h>
15 #include <linux/percpu.h>
16 #include <linux/hardirq.h>
17 #include <asm/pmc.h>
18 #include <asm/machdep.h>
19 #include <asm/firmware.h>
20 #include <asm/ptrace.h>
21 #include <asm/code-patching.h>
22
23 #define PERF_8xx_ID_CPU_CYCLES          1
24 #define PERF_8xx_ID_HW_INSTRUCTIONS     2
25 #define PERF_8xx_ID_ITLB_LOAD_MISS      3
26 #define PERF_8xx_ID_DTLB_LOAD_MISS      4
27
28 #define C(x)    PERF_COUNT_HW_CACHE_##x
29 #define DTLB_LOAD_MISS  (C(DTLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16))
30 #define ITLB_LOAD_MISS  (C(ITLB) | (C(OP_READ) << 8) | (C(RESULT_MISS) << 16))
31
32 extern unsigned long itlb_miss_counter, dtlb_miss_counter;
33 extern atomic_t instruction_counter;
34
35 static atomic_t insn_ctr_ref;
36 static atomic_t itlb_miss_ref;
37 static atomic_t dtlb_miss_ref;
38
39 static s64 get_insn_ctr(void)
40 {
41         int ctr;
42         unsigned long counta;
43
44         do {
45                 ctr = atomic_read(&instruction_counter);
46                 counta = mfspr(SPRN_COUNTA);
47         } while (ctr != atomic_read(&instruction_counter));
48
49         return ((s64)ctr << 16) | (counta >> 16);
50 }
51
52 static int event_type(struct perf_event *event)
53 {
54         switch (event->attr.type) {
55         case PERF_TYPE_HARDWARE:
56                 if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES)
57                         return PERF_8xx_ID_CPU_CYCLES;
58                 if (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS)
59                         return PERF_8xx_ID_HW_INSTRUCTIONS;
60                 break;
61         case PERF_TYPE_HW_CACHE:
62                 if (event->attr.config == ITLB_LOAD_MISS)
63                         return PERF_8xx_ID_ITLB_LOAD_MISS;
64                 if (event->attr.config == DTLB_LOAD_MISS)
65                         return PERF_8xx_ID_DTLB_LOAD_MISS;
66                 break;
67         case PERF_TYPE_RAW:
68                 break;
69         default:
70                 return -ENOENT;
71         }
72         return -EOPNOTSUPP;
73 }
74
75 static int mpc8xx_pmu_event_init(struct perf_event *event)
76 {
77         int type = event_type(event);
78
79         if (type < 0)
80                 return type;
81         return 0;
82 }
83
84 static int mpc8xx_pmu_add(struct perf_event *event, int flags)
85 {
86         int type = event_type(event);
87         s64 val = 0;
88
89         if (type < 0)
90                 return type;
91
92         switch (type) {
93         case PERF_8xx_ID_CPU_CYCLES:
94                 val = get_tb();
95                 break;
96         case PERF_8xx_ID_HW_INSTRUCTIONS:
97                 if (atomic_inc_return(&insn_ctr_ref) == 1)
98                         mtspr(SPRN_ICTRL, 0xc0080007);
99                 val = get_insn_ctr();
100                 break;
101         case PERF_8xx_ID_ITLB_LOAD_MISS:
102                 if (atomic_inc_return(&itlb_miss_ref) == 1) {
103                         unsigned long target = patch_site_addr(&patch__itlbmiss_perf);
104
105                         patch_branch_site(&patch__itlbmiss_exit_1, target, 0);
106 #ifndef CONFIG_PIN_TLB_TEXT
107                         patch_branch_site(&patch__itlbmiss_exit_2, target, 0);
108 #endif
109                 }
110                 val = itlb_miss_counter;
111                 break;
112         case PERF_8xx_ID_DTLB_LOAD_MISS:
113                 if (atomic_inc_return(&dtlb_miss_ref) == 1) {
114                         unsigned long target = patch_site_addr(&patch__dtlbmiss_perf);
115
116                         patch_branch_site(&patch__dtlbmiss_exit_1, target, 0);
117                         patch_branch_site(&patch__dtlbmiss_exit_2, target, 0);
118                         patch_branch_site(&patch__dtlbmiss_exit_3, target, 0);
119                 }
120                 val = dtlb_miss_counter;
121                 break;
122         }
123         local64_set(&event->hw.prev_count, val);
124         return 0;
125 }
126
127 static void mpc8xx_pmu_read(struct perf_event *event)
128 {
129         int type = event_type(event);
130         s64 prev, val = 0, delta = 0;
131
132         if (type < 0)
133                 return;
134
135         do {
136                 prev = local64_read(&event->hw.prev_count);
137                 switch (type) {
138                 case PERF_8xx_ID_CPU_CYCLES:
139                         val = get_tb();
140                         delta = 16 * (val - prev);
141                         break;
142                 case PERF_8xx_ID_HW_INSTRUCTIONS:
143                         val = get_insn_ctr();
144                         delta = prev - val;
145                         if (delta < 0)
146                                 delta += 0x1000000000000LL;
147                         break;
148                 case PERF_8xx_ID_ITLB_LOAD_MISS:
149                         val = itlb_miss_counter;
150                         delta = (s64)((s32)val - (s32)prev);
151                         break;
152                 case PERF_8xx_ID_DTLB_LOAD_MISS:
153                         val = dtlb_miss_counter;
154                         delta = (s64)((s32)val - (s32)prev);
155                         break;
156                 }
157         } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
158
159         local64_add(delta, &event->count);
160 }
161
162 static void mpc8xx_pmu_del(struct perf_event *event, int flags)
163 {
164         /* mfspr r10, SPRN_SPRG_SCRATCH0 */
165         unsigned int insn = PPC_INST_MFSPR | __PPC_RS(R10) |
166                             __PPC_SPR(SPRN_SPRG_SCRATCH0);
167
168         mpc8xx_pmu_read(event);
169
170         /* If it was the last user, stop counting to avoid useles overhead */
171         switch (event_type(event)) {
172         case PERF_8xx_ID_CPU_CYCLES:
173                 break;
174         case PERF_8xx_ID_HW_INSTRUCTIONS:
175                 if (atomic_dec_return(&insn_ctr_ref) == 0)
176                         mtspr(SPRN_ICTRL, 7);
177                 break;
178         case PERF_8xx_ID_ITLB_LOAD_MISS:
179                 if (atomic_dec_return(&itlb_miss_ref) == 0) {
180                         patch_instruction_site(&patch__itlbmiss_exit_1, insn);
181 #ifndef CONFIG_PIN_TLB_TEXT
182                         patch_instruction_site(&patch__itlbmiss_exit_2, insn);
183 #endif
184                 }
185                 break;
186         case PERF_8xx_ID_DTLB_LOAD_MISS:
187                 if (atomic_dec_return(&dtlb_miss_ref) == 0) {
188                         patch_instruction_site(&patch__dtlbmiss_exit_1, insn);
189                         patch_instruction_site(&patch__dtlbmiss_exit_2, insn);
190                         patch_instruction_site(&patch__dtlbmiss_exit_3, insn);
191                 }
192                 break;
193         }
194 }
195
196 static struct pmu mpc8xx_pmu = {
197         .event_init     = mpc8xx_pmu_event_init,
198         .add            = mpc8xx_pmu_add,
199         .del            = mpc8xx_pmu_del,
200         .read           = mpc8xx_pmu_read,
201         .capabilities   = PERF_PMU_CAP_NO_INTERRUPT |
202                           PERF_PMU_CAP_NO_NMI,
203 };
204
205 static int init_mpc8xx_pmu(void)
206 {
207         mtspr(SPRN_ICTRL, 7);
208         mtspr(SPRN_CMPA, 0);
209         mtspr(SPRN_COUNTA, 0xffff);
210
211         return perf_pmu_register(&mpc8xx_pmu, "cpu", PERF_TYPE_RAW);
212 }
213
214 early_initcall(init_mpc8xx_pmu);