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 /*ARGSUSED*/ 85 void 86 fpc_common_node_setup(dev_info_t *dip, int *index_p) 87 { 88 char pathname[MAXPATHLEN]; 89 90 (void) ddi_pathname(dip, pathname); 91 node_data[this_node].name = 92 kmem_zalloc(strlen(pathname)+1, KM_SLEEP); 93 (void) strcpy(node_data[this_node].name, pathname); 94 mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL); 95 *index_p = this_node++; 96 } 97 98 int 99 fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail) 100 { 101 int i; 102 dev_info_t *dip; 103 104 *avail = 0; 105 106 for (i = 1; i < MAX_REG_TYPES; i++) { 107 first_reg_of_type[i] = 108 first_reg_of_type[i-1] + counters_per_type[i-1]; 109 } 110 111 /* 112 * Look thru first level of device tree only. 113 * Assume there can be no more than NUM_LEAVES nodes in the system. 114 */ 115 dip = ddi_root_node(); 116 for (dip = ddi_get_child(dip); 117 ((dip != NULL) && (this_node < NUM_LEAVES)); 118 dip = ddi_get_next_sibling(dip)) { 119 if (fpc_platform_node_init(dip, avail) != SUCCESS) 120 return (DDI_FAILURE); 121 } 122 123 return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE); 124 } 125 126 int 127 fpc_perfcnt_module_fini(dev_info_t *dip) 128 { 129 int i; 130 131 for (i = 0; i < NUM_LEAVES; i++) { 132 fpc_platform_node_fini(node_data[i].plat_data_p); 133 if (node_data[i].name != NULL) 134 kmem_free(node_data[i].name, 135 strlen(node_data[i].name) + 1); 136 mutex_destroy(&node_data[i].mutex); 137 } 138 139 fpc_platform_module_fini(dip); 140 return (DDI_SUCCESS); 141 } 142 143 char 144 *fpc_get_dev_name_by_number(int index) 145 { 146 return (node_data[index].name); 147 } 148 149 void * 150 fpc_get_platform_data_by_number(int index) 151 { 152 return (node_data[index].plat_data_p); 153 } 154 155 156 int 157 fpc_set_platform_data_by_number(int index, void *data_p) 158 { 159 node_data[index].plat_data_p = data_p; 160 return (SUCCESS); 161 } 162 163 164 static int 165 fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp) 166 { 167 *mutex_pp = &node_data[index].mutex; 168 return (SUCCESS); 169 } 170 171 172 static int 173 fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter) 174 { 175 FPC_DBG1( 176 "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n", 177 regtype, counter, counters_per_type[regtype]); 178 if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype])) 179 return (-1); 180 FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter); 181 return (first_reg_of_type[regtype] + counter); 182 } 183 184 185 /* 186 * Program a performance counter. 187 * 188 * reggroup is which type of counter. 189 * counter is the counter number. 190 * event is the event to program for that counter. 191 */ 192 int 193 fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup, 194 uint64_t new_events) 195 { 196 int counter_index; 197 fire_perfreg_handle_t firehdl; 198 kmutex_t *mutex_p; 199 uint64_t old_events; 200 int rval = SUCCESS; 201 uint64_t zero = 0ull; 202 int num_counters, counter; 203 204 FPC_DBG1("fpc_perfcnt_program enter:\n"); 205 FPC_DBG1(" devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n", 206 devnum, reggroup, new_events); 207 208 if ((firehdl = fpc_get_perfreg_handle(devnum)) == 209 (fire_perfreg_handle_t)-1) 210 return (EIO); 211 212 num_counters = counters_per_type[reggroup]; 213 214 if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) { 215 (void) fpc_free_counter_handle(firehdl); 216 return (EIO); 217 } 218 219 mutex_enter(mutex_p); 220 221 if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) != 222 SUCCESS) { 223 FPC_DBG1("Read of old event data failed, group:%d\n", reggroup); 224 goto done_pgm; 225 } 226 227 for (counter = 0; counter < num_counters; counter++) { 228 229 counter_index = fpc_get_counter_reg_index(reggroup, counter); 230 231 if ((old_events & event_field_mask[counter_index]) == 232 (new_events & event_field_mask[counter_index])) 233 continue; 234 235 FPC_DBG1("Zeroing counter %d\n", counter_index); 236 if ((rval = fpc_counter_io(firehdl, reggroup, counter_index, 237 &zero, IS_WRITE)) != SUCCESS) 238 goto done_pgm; 239 } 240 241 if (old_events != new_events) { 242 if ((rval = 243 fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) != 244 SUCCESS) { 245 FPC_DBG1("Write of new event data failed, group:%d\n", 246 reggroup); 247 goto done_pgm; 248 } 249 } 250 done_pgm: 251 mutex_exit(mutex_p); 252 (void) fpc_free_counter_handle(firehdl); 253 return (rval); 254 } 255 256 257 /* 258 * Read a performance counter. 259 * 260 * reggroup is which type of counter. 261 * event_p returns the event programmed for that counter. 262 * values returns the counter values. 263 */ 264 int 265 fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup, 266 uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS]) 267 { 268 fire_perfreg_handle_t firehdl; 269 int counter_index; 270 kmutex_t *mutex_p; 271 int rval; 272 int num_counters, counter; 273 274 FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum); 275 num_counters = counters_per_type[reggroup]; 276 277 if ((firehdl = fpc_get_perfreg_handle(devnum)) == 278 (fire_perfreg_handle_t)-1) 279 return (EIO); 280 281 if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) 282 return (EIO); 283 284 mutex_enter(mutex_p); 285 286 if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) != 287 SUCCESS) 288 goto done_read; 289 290 for (counter = 0; counter < num_counters; counter++) { 291 counter_index = fpc_get_counter_reg_index(reggroup, counter); 292 293 if ((rval = fpc_counter_io(firehdl, reggroup, counter_index, 294 &values[counter], IS_READ)) != SUCCESS) 295 goto done_read; 296 297 FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%" 298 PRIx64 "\n", reggroup, counter, rval, values[counter]); 299 } 300 301 done_read: 302 mutex_exit(mutex_p); 303 (void) fpc_free_counter_handle(firehdl); 304 return (rval); 305 } 306