Linux-2.6.12-rc2
[cascardo/linux.git] / arch / ppc / oprofile / op_model_fsl_booke.c
1 /*
2  * oprofile/op_model_e500.c
3  *
4  * Freescale Book-E oprofile support, based on ppc64 oprofile support
5  * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
6  *
7  * Copyright (c) 2004 Freescale Semiconductor, Inc
8  *
9  * Author: Andy Fleming
10  * Maintainer: Kumar Gala <Kumar.Gala@freescale.com>
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version
15  * 2 of the License, or (at your option) any later version.
16  */
17
18 #include <linux/oprofile.h>
19 #include <linux/init.h>
20 #include <linux/smp.h>
21 #include <asm/ptrace.h>
22 #include <asm/system.h>
23 #include <asm/processor.h>
24 #include <asm/cputable.h>
25 #include <asm/reg_booke.h>
26 #include <asm/page.h>
27 #include <asm/perfmon.h>
28
29 #include "op_impl.h"
30
31 static unsigned long reset_value[OP_MAX_COUNTER];
32
33 static int num_counters;
34 static int oprofile_running;
35
36 static inline unsigned int ctr_read(unsigned int i)
37 {
38         switch(i) {
39                 case 0:
40                         return mfpmr(PMRN_PMC0);
41                 case 1:
42                         return mfpmr(PMRN_PMC1);
43                 case 2:
44                         return mfpmr(PMRN_PMC2);
45                 case 3:
46                         return mfpmr(PMRN_PMC3);
47                 default:
48                         return 0;
49         }
50 }
51
52 static inline void ctr_write(unsigned int i, unsigned int val)
53 {
54         switch(i) {
55                 case 0:
56                         mtpmr(PMRN_PMC0, val);
57                         break;
58                 case 1:
59                         mtpmr(PMRN_PMC1, val);
60                         break;
61                 case 2:
62                         mtpmr(PMRN_PMC2, val);
63                         break;
64                 case 3:
65                         mtpmr(PMRN_PMC3, val);
66                         break;
67                 default:
68                         break;
69         }
70 }
71
72
73 static void fsl_booke_reg_setup(struct op_counter_config *ctr,
74                              struct op_system_config *sys,
75                              int num_ctrs)
76 {
77         int i;
78
79         num_counters = num_ctrs;
80
81         /* freeze all counters */
82         pmc_stop_ctrs();
83
84         /* Our counters count up, and "count" refers to
85          * how much before the next interrupt, and we interrupt
86          * on overflow.  So we calculate the starting value
87          * which will give us "count" until overflow.
88          * Then we set the events on the enabled counters */
89         for (i = 0; i < num_counters; ++i) {
90                 reset_value[i] = 0x80000000UL - ctr[i].count;
91
92                 init_pmc_stop(i);
93
94                 set_pmc_event(i, ctr[i].event);
95
96                 set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel);
97         }
98 }
99
100 static void fsl_booke_start(struct op_counter_config *ctr)
101 {
102         int i;
103
104         mtmsr(mfmsr() | MSR_PMM);
105
106         for (i = 0; i < num_counters; ++i) {
107                 if (ctr[i].enabled) {
108                         ctr_write(i, reset_value[i]);
109                         /* Set Each enabled counterd to only
110                          * count when the Mark bit is not set */
111                         set_pmc_marked(i, 1, 0);
112                         pmc_start_ctr(i, 1);
113                 } else {
114                         ctr_write(i, 0);
115
116                         /* Set the ctr to be stopped */
117                         pmc_start_ctr(i, 0);
118                 }
119         }
120
121         /* Clear the freeze bit, and enable the interrupt.
122          * The counters won't actually start until the rfi clears
123          * the PMM bit */
124         pmc_start_ctrs(1);
125
126         oprofile_running = 1;
127
128         pr_debug("start on cpu %d, pmgc0 %x\n", smp_processor_id(),
129                         mfpmr(PMRN_PMGC0));
130 }
131
132 static void fsl_booke_stop(void)
133 {
134         /* freeze counters */
135         pmc_stop_ctrs();
136
137         oprofile_running = 0;
138
139         pr_debug("stop on cpu %d, pmgc0 %x\n", smp_processor_id(),
140                         mfpmr(PMRN_PMGC0));
141
142         mb();
143 }
144
145
146 static void fsl_booke_handle_interrupt(struct pt_regs *regs,
147                                     struct op_counter_config *ctr)
148 {
149         unsigned long pc;
150         int is_kernel;
151         int val;
152         int i;
153
154         /* set the PMM bit (see comment below) */
155         mtmsr(mfmsr() | MSR_PMM);
156
157         pc = regs->nip;
158         is_kernel = (pc >= KERNELBASE);
159
160         for (i = 0; i < num_counters; ++i) {
161                 val = ctr_read(i);
162                 if (val < 0) {
163                         if (oprofile_running && ctr[i].enabled) {
164                                 oprofile_add_pc(pc, is_kernel, i);
165                                 ctr_write(i, reset_value[i]);
166                         } else {
167                                 ctr_write(i, 0);
168                         }
169                 }
170         }
171
172         /* The freeze bit was set by the interrupt. */
173         /* Clear the freeze bit, and reenable the interrupt.
174          * The counters won't actually start until the rfi clears
175          * the PMM bit */
176         pmc_start_ctrs(1);
177 }
178
179 struct op_ppc32_model op_model_fsl_booke = {
180         .reg_setup              = fsl_booke_reg_setup,
181         .start                  = fsl_booke_start,
182         .stop                   = fsl_booke_stop,
183         .handle_interrupt       = fsl_booke_handle_interrupt,
184 };