Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | /** * @file arch/alpha/oprofile/op_model_ev5.c * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author Richard Henderson <rth@twiddle.net> */ #include <linux/oprofile.h> #include <linux/init.h> #include <linux/smp.h> #include <asm/ptrace.h> #include <asm/system.h> #include "op_impl.h" /* Compute all of the registers in preparation for enabling profiling. The 21164 (EV5) and 21164PC (PCA65) vary in the bit placement and meaning of the "CBOX" events. Given that we don't care about meaning at this point, arrange for the difference in bit placement to be handled by common code. */ static void common_reg_setup(struct op_register_config *reg, struct op_counter_config *ctr, struct op_system_config *sys, int cbox1_ofs, int cbox2_ofs) { int i, ctl, reset, need_reset; /* Select desired events. The event numbers are selected such that they map directly into the event selection fields: PCSEL0: 0, 1 PCSEL1: 24-39 CBOX1: 40-47 PCSEL2: 48-63 CBOX2: 64-71 There are two special cases, in that CYCLES can be measured on PCSEL[02], and SCACHE_WRITE can be measured on CBOX[12]. These event numbers are canonicalizes to their first appearance. */ ctl = 0; for (i = 0; i < 3; ++i) { unsigned long event = ctr[i].event; if (!ctr[i].enabled) continue; /* Remap the duplicate events, as described above. */ if (i == 2) { if (event == 0) event = 12+48; else if (event == 2+41) event = 4+65; } /* Convert the event numbers onto mux_select bit mask. */ if (event < 2) ctl |= event << 31; else if (event < 24) /* error */; else if (event < 40) ctl |= (event - 24) << 4; else if (event < 48) ctl |= (event - 40) << cbox1_ofs | 15 << 4; else if (event < 64) ctl |= event - 48; else if (event < 72) ctl |= (event - 64) << cbox2_ofs | 15; } reg->mux_select = ctl; /* Select processor mode. */ /* ??? Need to come up with some mechanism to trace only selected processes. For now select from pal, kernel and user mode. */ ctl = 0; ctl |= !sys->enable_pal << 9; ctl |= !sys->enable_kernel << 8; ctl |= !sys->enable_user << 30; reg->proc_mode = ctl; /* Select interrupt frequencies. Take the interrupt count selected by the user, and map it onto one of the possible counter widths. If the user value is in between, compute a value to which the counter is reset at each interrupt. */ ctl = reset = need_reset = 0; for (i = 0; i < 3; ++i) { unsigned long max, hilo, count = ctr[i].count; if (!ctr[i].enabled) continue; if (count <= 256) count = 256, hilo = 3, max = 256; else { max = (i == 2 ? 16384 : 65536); hilo = 2; if (count > max) count = max; } ctr[i].count = count; ctl |= hilo << (8 - i*2); reset |= (max - count) << (48 - 16*i); if (count != max) need_reset |= 1 << i; } reg->freq = ctl; reg->reset_values = reset; reg->need_reset = need_reset; } static void ev5_reg_setup(struct op_register_config *reg, struct op_counter_config *ctr, struct op_system_config *sys) { common_reg_setup(reg, ctr, sys, 19, 22); } static void pca56_reg_setup(struct op_register_config *reg, struct op_counter_config *ctr, struct op_system_config *sys) { common_reg_setup(reg, ctr, sys, 8, 11); } /* Program all of the registers in preparation for enabling profiling. */ static void ev5_cpu_setup (void *x) { struct op_register_config *reg = x; wrperfmon(2, reg->mux_select); wrperfmon(3, reg->proc_mode); wrperfmon(4, reg->freq); wrperfmon(6, reg->reset_values); } /* CTR is a counter for which the user has requested an interrupt count in between one of the widths selectable in hardware. Reset the count for CTR to the value stored in REG->RESET_VALUES. For EV5, this means disabling profiling, reading the current values, masking in the value for the desired register, writing, then turning profiling back on. This can be streamlined if profiling is only enabled for user mode. In that case we know that the counters are not currently incrementing (due to being in kernel mode). */ static void ev5_reset_ctr(struct op_register_config *reg, unsigned long ctr) { unsigned long values, mask, not_pk, reset_values; mask = (ctr == 0 ? 0xfffful << 48 : ctr == 1 ? 0xfffful << 32 : 0x3fff << 16); not_pk = 1 << 9 | 1 << 8; reset_values = reg->reset_values; if ((reg->proc_mode & not_pk) == not_pk) { values = wrperfmon(5, 0); values = (reset_values & mask) | (values & ~mask & -2); wrperfmon(6, values); } else { wrperfmon(0, -1); values = wrperfmon(5, 0); values = (reset_values & mask) | (values & ~mask & -2); wrperfmon(6, values); wrperfmon(1, reg->enable); } } static void ev5_handle_interrupt(unsigned long which, struct pt_regs *regs, struct op_counter_config *ctr) { /* Record the sample. */ oprofile_add_sample(regs, which); } struct op_axp_model op_model_ev5 = { .reg_setup = ev5_reg_setup, .cpu_setup = ev5_cpu_setup, .reset_ctr = ev5_reset_ctr, .handle_interrupt = ev5_handle_interrupt, .cpu_type = "alpha/ev5", .num_counters = 3, .can_set_proc_mode = 1, }; struct op_axp_model op_model_pca56 = { .reg_setup = pca56_reg_setup, .cpu_setup = ev5_cpu_setup, .reset_ctr = ev5_reset_ctr, .handle_interrupt = ev5_handle_interrupt, .cpu_type = "alpha/pca56", .num_counters = 3, .can_set_proc_mode = 1, }; |