1110e73f9Sschwartz /* 2110e73f9Sschwartz * CDDL HEADER START 3110e73f9Sschwartz * 4110e73f9Sschwartz * The contents of this file are subject to the terms of the 5110e73f9Sschwartz * Common Development and Distribution License (the "License"). 6110e73f9Sschwartz * You may not use this file except in compliance with the License. 7110e73f9Sschwartz * 8110e73f9Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9110e73f9Sschwartz * or http://www.opensolaris.org/os/licensing. 10110e73f9Sschwartz * See the License for the specific language governing permissions 11110e73f9Sschwartz * and limitations under the License. 12110e73f9Sschwartz * 13110e73f9Sschwartz * When distributing Covered Code, include this CDDL HEADER in each 14110e73f9Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15110e73f9Sschwartz * If applicable, add the following below this CDDL HEADER, with the 16110e73f9Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying 17110e73f9Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner] 18110e73f9Sschwartz * 19110e73f9Sschwartz * CDDL HEADER END 20110e73f9Sschwartz */ 21110e73f9Sschwartz 22110e73f9Sschwartz /* 23110e73f9Sschwartz * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24110e73f9Sschwartz * Use is subject to license terms. 25110e73f9Sschwartz */ 26110e73f9Sschwartz 27110e73f9Sschwartz #pragma ident "%Z%%M% %I% %E% SMI" 28110e73f9Sschwartz 29110e73f9Sschwartz #include <sys/param.h> 30110e73f9Sschwartz #include <sys/types.h> 31110e73f9Sschwartz #include <sys/errno.h> 32110e73f9Sschwartz #include <sys/kmem.h> 33110e73f9Sschwartz #include <sys/sunddi.h> 34110e73f9Sschwartz #include <sys/disp.h> 35110e73f9Sschwartz #include <fpc.h> 36110e73f9Sschwartz #include <fpc-impl.h> 37110e73f9Sschwartz 38110e73f9Sschwartz #define BOUNDS_CHECK_FAILS(arg, max) ((arg < 0) && (arg >= max)) 39110e73f9Sschwartz 40110e73f9Sschwartz static int this_node = 0; 41110e73f9Sschwartz node_data_t node_data[NUM_LEAVES]; 42110e73f9Sschwartz 43110e73f9Sschwartz int fpc_debug = 0; 44110e73f9Sschwartz 45110e73f9Sschwartz static int counters_per_type[MAX_REG_TYPES] = { 46110e73f9Sschwartz NUM_JBC_COUNTERS, 47110e73f9Sschwartz NUM_IMU_COUNTERS, 48110e73f9Sschwartz NUM_MMU_COUNTERS, 49110e73f9Sschwartz NUM_TLU_COUNTERS, 50110e73f9Sschwartz NUM_LPU_COUNTERS 51110e73f9Sschwartz }; 52110e73f9Sschwartz 53110e73f9Sschwartz static int first_reg_of_type[MAX_REG_TYPES]; 54110e73f9Sschwartz 55110e73f9Sschwartz static uint64_t event_field_mask[NUM_TOTAL_COUNTERS] = { 56110e73f9Sschwartz JBC_PIC0_EVT_MASK, /* JBC counter 0 */ 57110e73f9Sschwartz JBC_PIC1_EVT_MASK, /* JBC counter 1 */ 58110e73f9Sschwartz IMU_PIC0_EVT_MASK, /* IMU counter 0 */ 59110e73f9Sschwartz IMU_PIC1_EVT_MASK, /* IMU counter 1 */ 60110e73f9Sschwartz MMU_PIC0_EVT_MASK, /* MMU counter 0 */ 61110e73f9Sschwartz MMU_PIC1_EVT_MASK, /* MMU counter 1 */ 62110e73f9Sschwartz TLU_PIC0_EVT_MASK, /* TLU counter 0 */ 63110e73f9Sschwartz TLU_PIC1_EVT_MASK, /* TLU counter 1 */ 64110e73f9Sschwartz TLU_PIC2_EVT_MASK, /* TLU counter 2 */ 65110e73f9Sschwartz LPU_PIC0_EVT_MASK, /* LPU counter 1 */ 66110e73f9Sschwartz LPU_PIC1_EVT_MASK /* LPU counter 2 */ 67110e73f9Sschwartz }; 68110e73f9Sschwartz 69110e73f9Sschwartz /* Offsets of the fields shown in event_field_masks. */ 70110e73f9Sschwartz static int event_field_offset[NUM_TOTAL_COUNTERS] = { 71110e73f9Sschwartz PIC0_EVT_SEL_SHIFT, /* JBC counter 0 */ 72110e73f9Sschwartz PIC1_EVT_SEL_SHIFT, /* JBC counter 1 */ 73110e73f9Sschwartz PIC0_EVT_SEL_SHIFT, /* IMU counter 0 */ 74110e73f9Sschwartz PIC1_EVT_SEL_SHIFT, /* IMU counter 1 */ 75110e73f9Sschwartz PIC0_EVT_SEL_SHIFT, /* MMU counter 0 */ 76110e73f9Sschwartz PIC1_EVT_SEL_SHIFT, /* MMU counter 1 */ 77110e73f9Sschwartz PIC0_EVT_SEL_SHIFT, /* TLU counter 0 */ 78110e73f9Sschwartz PIC1_EVT_SEL_SHIFT, /* TLU counter 1 */ 79110e73f9Sschwartz PIC2_EVT_SEL_SHIFT, /* TLU counter 2 */ 80110e73f9Sschwartz PIC0_EVT_SEL_SHIFT, /* LPU counter 1 */ 81110e73f9Sschwartz PIC2_EVT_SEL_SHIFT /* LPU counter 2 */ 82110e73f9Sschwartz }; 83110e73f9Sschwartz 84*0ad689d6Sschwartz /* For determining platform suitability at _init time. */ 85*0ad689d6Sschwartz int 86*0ad689d6Sschwartz fpc_init_platform_check() 87*0ad689d6Sschwartz { 88*0ad689d6Sschwartz return (fpc_platform_check()); 89*0ad689d6Sschwartz } 90*0ad689d6Sschwartz 91110e73f9Sschwartz /*ARGSUSED*/ 92110e73f9Sschwartz void 93110e73f9Sschwartz fpc_common_node_setup(dev_info_t *dip, int *index_p) 94110e73f9Sschwartz { 95110e73f9Sschwartz char pathname[MAXPATHLEN]; 96110e73f9Sschwartz 97110e73f9Sschwartz (void) ddi_pathname(dip, pathname); 98110e73f9Sschwartz node_data[this_node].name = 99110e73f9Sschwartz kmem_zalloc(strlen(pathname)+1, KM_SLEEP); 100110e73f9Sschwartz (void) strcpy(node_data[this_node].name, pathname); 101110e73f9Sschwartz mutex_init(&node_data[this_node].mutex, NULL, MUTEX_DRIVER, NULL); 102110e73f9Sschwartz *index_p = this_node++; 103110e73f9Sschwartz } 104110e73f9Sschwartz 105110e73f9Sschwartz int 106110e73f9Sschwartz fpc_perfcnt_module_init(dev_info_t *fpc_dip, int *avail) 107110e73f9Sschwartz { 108110e73f9Sschwartz int i; 109110e73f9Sschwartz dev_info_t *dip; 110110e73f9Sschwartz 111110e73f9Sschwartz *avail = 0; 112110e73f9Sschwartz 113110e73f9Sschwartz for (i = 1; i < MAX_REG_TYPES; i++) { 114110e73f9Sschwartz first_reg_of_type[i] = 115110e73f9Sschwartz first_reg_of_type[i-1] + counters_per_type[i-1]; 116110e73f9Sschwartz } 117110e73f9Sschwartz 118110e73f9Sschwartz /* 119110e73f9Sschwartz * Look thru first level of device tree only. 120110e73f9Sschwartz * Assume there can be no more than NUM_LEAVES nodes in the system. 121110e73f9Sschwartz */ 122110e73f9Sschwartz dip = ddi_root_node(); 123110e73f9Sschwartz for (dip = ddi_get_child(dip); 124110e73f9Sschwartz ((dip != NULL) && (this_node < NUM_LEAVES)); 125110e73f9Sschwartz dip = ddi_get_next_sibling(dip)) { 126110e73f9Sschwartz if (fpc_platform_node_init(dip, avail) != SUCCESS) 127110e73f9Sschwartz return (DDI_FAILURE); 128110e73f9Sschwartz } 129110e73f9Sschwartz 130110e73f9Sschwartz return ((*avail) ? fpc_platform_module_init(fpc_dip) : DDI_FAILURE); 131110e73f9Sschwartz } 132110e73f9Sschwartz 133110e73f9Sschwartz int 134110e73f9Sschwartz fpc_perfcnt_module_fini(dev_info_t *dip) 135110e73f9Sschwartz { 136110e73f9Sschwartz int i; 137110e73f9Sschwartz 138110e73f9Sschwartz for (i = 0; i < NUM_LEAVES; i++) { 139110e73f9Sschwartz fpc_platform_node_fini(node_data[i].plat_data_p); 140*0ad689d6Sschwartz if (node_data[i].name != NULL) { 141110e73f9Sschwartz kmem_free(node_data[i].name, 142110e73f9Sschwartz strlen(node_data[i].name) + 1); 143110e73f9Sschwartz mutex_destroy(&node_data[i].mutex); 144110e73f9Sschwartz } 145*0ad689d6Sschwartz } 146110e73f9Sschwartz 147110e73f9Sschwartz fpc_platform_module_fini(dip); 148110e73f9Sschwartz return (DDI_SUCCESS); 149110e73f9Sschwartz } 150110e73f9Sschwartz 151110e73f9Sschwartz char 152110e73f9Sschwartz *fpc_get_dev_name_by_number(int index) 153110e73f9Sschwartz { 154110e73f9Sschwartz return (node_data[index].name); 155110e73f9Sschwartz } 156110e73f9Sschwartz 157110e73f9Sschwartz void * 158110e73f9Sschwartz fpc_get_platform_data_by_number(int index) 159110e73f9Sschwartz { 160110e73f9Sschwartz return (node_data[index].plat_data_p); 161110e73f9Sschwartz } 162110e73f9Sschwartz 163110e73f9Sschwartz 164110e73f9Sschwartz int 165110e73f9Sschwartz fpc_set_platform_data_by_number(int index, void *data_p) 166110e73f9Sschwartz { 167110e73f9Sschwartz node_data[index].plat_data_p = data_p; 168110e73f9Sschwartz return (SUCCESS); 169110e73f9Sschwartz } 170110e73f9Sschwartz 171110e73f9Sschwartz 172110e73f9Sschwartz static int 173110e73f9Sschwartz fpc_get_mutex_by_number(int index, kmutex_t **mutex_pp) 174110e73f9Sschwartz { 175110e73f9Sschwartz *mutex_pp = &node_data[index].mutex; 176110e73f9Sschwartz return (SUCCESS); 177110e73f9Sschwartz } 178110e73f9Sschwartz 179110e73f9Sschwartz 180110e73f9Sschwartz static int 181110e73f9Sschwartz fpc_get_counter_reg_index(fire_perfcnt_t regtype, int counter) 182110e73f9Sschwartz { 183110e73f9Sschwartz FPC_DBG1( 184110e73f9Sschwartz "fpc_get_counter_reg_index: regtype:%d, counter:%d, bounds:%d\n", 185110e73f9Sschwartz regtype, counter, counters_per_type[regtype]); 186110e73f9Sschwartz if (BOUNDS_CHECK_FAILS(counter, counters_per_type[regtype])) 187110e73f9Sschwartz return (-1); 188110e73f9Sschwartz FPC_DBG1("returning: %d\n", first_reg_of_type[regtype] + counter); 189110e73f9Sschwartz return (first_reg_of_type[regtype] + counter); 190110e73f9Sschwartz } 191110e73f9Sschwartz 192110e73f9Sschwartz 193110e73f9Sschwartz /* 194110e73f9Sschwartz * Program a performance counter. 195110e73f9Sschwartz * 196110e73f9Sschwartz * reggroup is which type of counter. 197110e73f9Sschwartz * counter is the counter number. 198110e73f9Sschwartz * event is the event to program for that counter. 199110e73f9Sschwartz */ 200110e73f9Sschwartz int 201110e73f9Sschwartz fpc_perfcnt_program(int devnum, fire_perfcnt_t reggroup, 202110e73f9Sschwartz uint64_t new_events) 203110e73f9Sschwartz { 204110e73f9Sschwartz int counter_index; 205110e73f9Sschwartz fire_perfreg_handle_t firehdl; 206110e73f9Sschwartz kmutex_t *mutex_p; 207110e73f9Sschwartz uint64_t old_events; 208110e73f9Sschwartz int rval = SUCCESS; 209110e73f9Sschwartz uint64_t zero = 0ull; 210110e73f9Sschwartz int num_counters, counter; 211110e73f9Sschwartz 212110e73f9Sschwartz FPC_DBG1("fpc_perfcnt_program enter:\n"); 213110e73f9Sschwartz FPC_DBG1(" devnum:%d, reggroup:%d, new_events:0x%" PRIx64 "\n", 214110e73f9Sschwartz devnum, reggroup, new_events); 215110e73f9Sschwartz 216110e73f9Sschwartz if ((firehdl = fpc_get_perfreg_handle(devnum)) == 217110e73f9Sschwartz (fire_perfreg_handle_t)-1) 218110e73f9Sschwartz return (EIO); 219110e73f9Sschwartz 220110e73f9Sschwartz num_counters = counters_per_type[reggroup]; 221110e73f9Sschwartz 222110e73f9Sschwartz if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) { 223110e73f9Sschwartz (void) fpc_free_counter_handle(firehdl); 224110e73f9Sschwartz return (EIO); 225110e73f9Sschwartz } 226110e73f9Sschwartz 227110e73f9Sschwartz mutex_enter(mutex_p); 228110e73f9Sschwartz 229110e73f9Sschwartz if ((rval = fpc_event_io(firehdl, reggroup, &old_events, IS_READ)) != 230110e73f9Sschwartz SUCCESS) { 231110e73f9Sschwartz FPC_DBG1("Read of old event data failed, group:%d\n", reggroup); 232110e73f9Sschwartz goto done_pgm; 233110e73f9Sschwartz } 234110e73f9Sschwartz 235110e73f9Sschwartz for (counter = 0; counter < num_counters; counter++) { 236110e73f9Sschwartz 237110e73f9Sschwartz counter_index = fpc_get_counter_reg_index(reggroup, counter); 238110e73f9Sschwartz 239110e73f9Sschwartz if ((old_events & event_field_mask[counter_index]) == 240110e73f9Sschwartz (new_events & event_field_mask[counter_index])) 241110e73f9Sschwartz continue; 242110e73f9Sschwartz 243110e73f9Sschwartz FPC_DBG1("Zeroing counter %d\n", counter_index); 244110e73f9Sschwartz if ((rval = fpc_counter_io(firehdl, reggroup, counter_index, 245110e73f9Sschwartz &zero, IS_WRITE)) != SUCCESS) 246110e73f9Sschwartz goto done_pgm; 247110e73f9Sschwartz } 248110e73f9Sschwartz 249110e73f9Sschwartz if (old_events != new_events) { 250110e73f9Sschwartz if ((rval = 251110e73f9Sschwartz fpc_event_io(firehdl, reggroup, &new_events, IS_WRITE)) != 252110e73f9Sschwartz SUCCESS) { 253110e73f9Sschwartz FPC_DBG1("Write of new event data failed, group:%d\n", 254110e73f9Sschwartz reggroup); 255110e73f9Sschwartz goto done_pgm; 256110e73f9Sschwartz } 257110e73f9Sschwartz } 258110e73f9Sschwartz done_pgm: 259110e73f9Sschwartz mutex_exit(mutex_p); 260110e73f9Sschwartz (void) fpc_free_counter_handle(firehdl); 261110e73f9Sschwartz return (rval); 262110e73f9Sschwartz } 263110e73f9Sschwartz 264110e73f9Sschwartz 265110e73f9Sschwartz /* 266110e73f9Sschwartz * Read a performance counter. 267110e73f9Sschwartz * 268110e73f9Sschwartz * reggroup is which type of counter. 269110e73f9Sschwartz * event_p returns the event programmed for that counter. 270110e73f9Sschwartz * values returns the counter values. 271110e73f9Sschwartz */ 272110e73f9Sschwartz int 273110e73f9Sschwartz fpc_perfcnt_read(int devnum, fire_perfcnt_t reggroup, 274110e73f9Sschwartz uint64_t *event_p, uint64_t values[NUM_MAX_COUNTERS]) 275110e73f9Sschwartz { 276110e73f9Sschwartz fire_perfreg_handle_t firehdl; 277110e73f9Sschwartz int counter_index; 278110e73f9Sschwartz kmutex_t *mutex_p; 279110e73f9Sschwartz int rval; 280110e73f9Sschwartz int num_counters, counter; 281110e73f9Sschwartz 282110e73f9Sschwartz FPC_DBG1("fpc_perfcnt_read: devnum:%d\n", devnum); 283110e73f9Sschwartz num_counters = counters_per_type[reggroup]; 284110e73f9Sschwartz 285110e73f9Sschwartz if ((firehdl = fpc_get_perfreg_handle(devnum)) == 286110e73f9Sschwartz (fire_perfreg_handle_t)-1) 287110e73f9Sschwartz return (EIO); 288110e73f9Sschwartz 289110e73f9Sschwartz if (fpc_get_mutex_by_number(devnum, &mutex_p) != DDI_SUCCESS) 290110e73f9Sschwartz return (EIO); 291110e73f9Sschwartz 292110e73f9Sschwartz mutex_enter(mutex_p); 293110e73f9Sschwartz 294110e73f9Sschwartz if ((rval = fpc_event_io(firehdl, reggroup, event_p, IS_READ)) != 295110e73f9Sschwartz SUCCESS) 296110e73f9Sschwartz goto done_read; 297110e73f9Sschwartz 298110e73f9Sschwartz for (counter = 0; counter < num_counters; counter++) { 299110e73f9Sschwartz counter_index = fpc_get_counter_reg_index(reggroup, counter); 300110e73f9Sschwartz 301110e73f9Sschwartz if ((rval = fpc_counter_io(firehdl, reggroup, counter_index, 302110e73f9Sschwartz &values[counter], IS_READ)) != SUCCESS) 303110e73f9Sschwartz goto done_read; 304110e73f9Sschwartz 305110e73f9Sschwartz FPC_DBG1("Read_counter %d / %d, status:%d, value returned:0x%" 306110e73f9Sschwartz PRIx64 "\n", reggroup, counter, rval, values[counter]); 307110e73f9Sschwartz } 308110e73f9Sschwartz 309110e73f9Sschwartz done_read: 310110e73f9Sschwartz mutex_exit(mutex_p); 311110e73f9Sschwartz (void) fpc_free_counter_handle(firehdl); 312110e73f9Sschwartz return (rval); 313110e73f9Sschwartz } 314