1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/param.h> 30 #include <sys/types.h> 31 #include <sys/errno.h> 32 #include <sys/kmem.h> 33 #include <sys/sunddi.h> 34 #include <sys/disp.h> 35 #include <fpc.h> 36 #include <fpc-impl.h> 37 38 #define BOUNDS_CHECK_FAILS(arg, max) ((arg < 0) && (arg >= max)) 39 40 static int this_node = 0; 41 node_data_t node_data[NUM_LEAVES]; 42 43 int fpc_debug = 0; 44 45 static int counters_per_type[MAX_REG_TYPES] = { 46 NUM_JBC_COUNTERS, 47 NUM_IMU_COUNTERS, 48 NUM_MMU_COUNTERS, 49 NUM_TLU_COUNTERS, 50 NUM_LPU_COUNTERS 51 }; 52 53 static int first_reg_of_type[MAX_REG_TYPES]; 54 55 static uint64_t event_field_mask[NUM_TOTAL_COUNTERS] = { 56 JBC_PIC0_EVT_MASK, /* JBC counter 0 */ 57 JBC_PIC1_EVT_MASK, /* JBC counter 1 */ 58 IMU_PIC0_EVT_MASK, /* IMU counter 0 */ 59 IMU_PIC1_EVT_MASK, /* IMU counter 1 */ 60 MMU_PIC0_EVT_MASK, /* MMU counter 0 */ 61 MMU_PIC1_EVT_MASK, /* MMU counter 1 */ 62 TLU_PIC0_EVT_MASK, /* TLU counter 0 */ 63 TLU_PIC1_EVT_MASK, /* TLU counter 1 */ 64 TLU_PIC2_EVT_MASK, /* TLU counter 2 */ 65 LPU_PIC0_EVT_MASK, /* LPU counter 1 */ 66 LPU_PIC1_EVT_MASK /* LPU counter 2 */ 67 }; 68 69 /* Offsets of the fields shown in event_field_masks. */ 70 static int event_field_offset[NUM_TOTAL_COUNTERS] = { 71 PIC0_EVT_SEL_SHIFT, /* JBC counter 0 */ 72 PIC1_EVT_SEL_SHIFT, /* JBC counter 1 */ 73 PIC0_EVT_SEL_SHIFT, /* IMU counter 0 */ 74 PIC1_EVT_SEL_SHIFT, /* IMU counter 1 */ 75 PIC0_EVT_SEL_SHIFT, /* MMU counter 0 */ 76 PIC1_EVT_SEL_SHIFT, /* MMU counter 1 */ 77 PIC0_EVT_SEL_SHIFT, /* TLU counter 0 */ 78 PIC1_EVT_SEL_SHIFT, /* TLU counter 1 */ 79 PIC2_EVT_SEL_SHIFT, /* TLU counter 2 */ 80 PIC0_EVT_SEL_SHIFT, /* LPU counter 1 */ 81 PIC2_EVT_SEL_SHIFT /* LPU counter 2 */ 82 }; 83 84 /* For determining platform suitability at _init time. */ 85 int 86 fpc_init_platform_check() 87 { 88 return (fpc_platform_check()); 89 } 90 91 /*ARGSUSED*/ 92 void 93 fpc_common_node_setup(dev_info_t *dip, int *index_p) 94 { 95 char pathname[MAXPATHLEN]; 96 97 (void) ddi_pathname(dip, pathname); 98 node_data[this_node].name = 99 kmem_zalloc(strlen(pathname)+1, KM_SLEEP); 100 (void) strcpy(node_data[this_node].name, pathname); 101 mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL); 102 *index_p = this_node++; 103 } 104 105 int 106 fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail) 107 { 108 int i; 109 dev_info_t *dip; 110 111 *avail = 0; 112 113 for (i = 1; i < MAX_REG_TYPES; i++) { 114 first_reg_of_type[i] = 115 first_reg_of_type[i-1] + counters_per_type[i-1]; 116 } 117 118 /* 119 * Look thru first level of device tree only. 120 * Assume there can be no more than NUM_LEAVES nodes in the system. 121 */ 122 dip = ddi_root_node(); 123 for (dip = ddi_get_child(dip); 124 ((dip != NULL) && (this_node < NUM_LEAVES)); 125 dip = ddi_get_next_sibling(dip)) { 126 if (fpc_platform_node_init(dip, avail) != SUCCESS) 127 return (DDI_FAILURE); 128 } 129 130 return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE); 131 } 132 133 int 134 fpc_perfcnt_module_fini(dev_info_t *dip) 135 { 136 int i; 137 138 for (i = 0; i < NUM_LEAVES; i++) { 139 fpc_platform_node_fini(node_data[i].plat_data_p); 140 if (node_data[i].name != NULL) { 141 kmem_free(node_data[i].name, 142 strlen(node_data[i].name) + 1); 143 mutex_destroy(&node_data[i].mutex); 144 } 145 } 146 147 fpc_platform_module_fini(dip); 148 return (DDI_SUCCESS); 149 } 150 151 char 152 *fpc_get_dev_name_by_number(int index) 153 { 154 return (node_data[index].name); 155 } 156 157 void * 158 fpc_get_platform_data_by_number(int index) 159 { 160 return (node_data[index].plat_data_p); 161 } 162 163 164 int 165 fpc_set_platform_data_by_number(int index, void *data_p) 166 { 167 node_data[index].plat_data_p = data_p; 168 return (SUCCESS); 169 } 170 171 172 static int 173 fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp) 174 { 175 *mutex_pp = &node_data[index].mutex; 176 return (SUCCESS); 177 } 178 179 180 static int 181 fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter) 182 { 183 FPC_DBG1( 184 "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n", 185 regtype, counter, counters_per_type[regtype]); 186 if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype])) 187 return (-1); 188 FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter); 189 return (first_reg_of_type[regtype] + counter); 190 } 191 192 193 /* 194 * Program a performance counter. 195 * 196 * reggroup is which type of counter. 197 * counter is the counter number. 198 * event is the event to program for that counter. 199 */ 200 int 201 fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup, 202 uint64_t new_events) 203 { 204 int counter_index; 205 fire_perfreg_handle_t firehdl; 206 kmutex_t *mutex_p; 207 uint64_t old_events; 208 int rval = SUCCESS; 209 uint64_t zero = 0ull; 210 int num_counters, counter; 211 212 FPC_DBG1("fpc_perfcnt_program enter:\n"); 213 FPC_DBG1(" devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n", 214 devnum, reggroup, new_events); 215 216 if ((firehdl = fpc_get_perfreg_handle(devnum)) == 217 (fire_perfreg_handle_t)-1) 218 return (EIO); 219 220 num_counters = counters_per_type[reggroup]; 221 222 if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) { 223 (void) fpc_free_counter_handle(firehdl); 224 return (EIO); 225 } 226 227 mutex_enter(mutex_p); 228 229 if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) != 230 SUCCESS) { 231 FPC_DBG1("Read of old event data failed, group:%d\n", reggroup); 232 goto done_pgm; 233 } 234 235 for (counter = 0; counter < num_counters; counter++) { 236 237 counter_index = fpc_get_counter_reg_index(reggroup, counter); 238 239 if ((old_events & event_field_mask[counter_index]) == 240 (new_events & event_field_mask[counter_index])) 241 continue; 242 243 FPC_DBG1("Zeroing counter %d\n", counter_index); 244 if ((rval = fpc_counter_io(firehdl, reggroup, counter_index, 245 &zero, IS_WRITE)) != SUCCESS) 246 goto done_pgm; 247 } 248 249 if (old_events != new_events) { 250 if ((rval = 251 fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) != 252 SUCCESS) { 253 FPC_DBG1("Write of new event data failed, group:%d\n", 254 reggroup); 255 goto done_pgm; 256 } 257 } 258 done_pgm: 259 mutex_exit(mutex_p); 260 (void) fpc_free_counter_handle(firehdl); 261 return (rval); 262 } 263 264 265 /* 266 * Read a performance counter. 267 * 268 * reggroup is which type of counter. 269 * event_p returns the event programmed for that counter. 270 * values returns the counter values. 271 */ 272 int 273 fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup, 274 uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS]) 275 { 276 fire_perfreg_handle_t firehdl; 277 int counter_index; 278 kmutex_t *mutex_p; 279 int rval; 280 int num_counters, counter; 281 282 FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum); 283 num_counters = counters_per_type[reggroup]; 284 285 if ((firehdl = fpc_get_perfreg_handle(devnum)) == 286 (fire_perfreg_handle_t)-1) 287 return (EIO); 288 289 if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) 290 return (EIO); 291 292 mutex_enter(mutex_p); 293 294 if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) != 295 SUCCESS) 296 goto done_read; 297 298 for (counter = 0; counter < num_counters; counter++) { 299 counter_index = fpc_get_counter_reg_index(reggroup, counter); 300 301 if ((rval = fpc_counter_io(firehdl, reggroup, counter_index, 302 &values[counter], IS_READ)) != SUCCESS) 303 goto done_read; 304 305 FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%" 306 PRIx64 "\n", reggroup, counter, rval, values[counter]); 307 } 308 309 done_read: 310 mutex_exit(mutex_p); 311 (void) fpc_free_counter_handle(firehdl); 312 return (rval); 313 } 314