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