1*4249Sschwartz /* 2*4249Sschwartz * CDDL HEADER START 3*4249Sschwartz * 4*4249Sschwartz * The contents of this file are subject to the terms of the 5*4249Sschwartz * Common Development and Distribution License (the "License"). 6*4249Sschwartz * You may not use this file except in compliance with the License. 7*4249Sschwartz * 8*4249Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*4249Sschwartz * or http://www.opensolaris.org/os/licensing. 10*4249Sschwartz * See the License for the specific language governing permissions 11*4249Sschwartz * and limitations under the License. 12*4249Sschwartz * 13*4249Sschwartz * When distributing Covered Code, include this CDDL HEADER in each 14*4249Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*4249Sschwartz * If applicable, add the following below this CDDL HEADER, with the 16*4249Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying 17*4249Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner] 18*4249Sschwartz * 19*4249Sschwartz * CDDL HEADER END 20*4249Sschwartz */ 21*4249Sschwartz 22*4249Sschwartz /* 23*4249Sschwartz * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*4249Sschwartz * Use is subject to license terms. 25*4249Sschwartz */ 26*4249Sschwartz 27*4249Sschwartz #pragma ident "%Z%%M% %I% %E% SMI" 28*4249Sschwartz 29*4249Sschwartz #include <sys/file.h> 30*4249Sschwartz #include <sys/sunndi.h> 31*4249Sschwartz #include <sys/sunddi.h> 32*4249Sschwartz #include <sys/sunldi.h> 33*4249Sschwartz #include <io/px/px_regs.h> 34*4249Sschwartz #include <sys/pci_tools.h> 35*4249Sschwartz #include <fpc.h> 36*4249Sschwartz #include <fpc-impl.h> 37*4249Sschwartz 38*4249Sschwartz #define CHIP_COMPATIBLE_NAME "pciex108e,80f0" 39*4249Sschwartz #define BANK_ADDR_MASK 0x7FFFFF 40*4249Sschwartz 41*4249Sschwartz #define OPEN_FLAGS (FREAD | FWRITE) 42*4249Sschwartz 43*4249Sschwartz #define PCIE_BANK 0 44*4249Sschwartz #define JBUS_BANK 1 45*4249Sschwartz 46*4249Sschwartz typedef struct px_regs { 47*4249Sschwartz uint32_t addr_hi; 48*4249Sschwartz uint32_t addr_lo; 49*4249Sschwartz uint32_t size_hi; 50*4249Sschwartz uint32_t size_lo; 51*4249Sschwartz } px_regs_t; 52*4249Sschwartz 53*4249Sschwartz /* There is one of these for every root nexus device found */ 54*4249Sschwartz typedef struct fire4u_specific { 55*4249Sschwartz char *nodename; 56*4249Sschwartz uintptr_t jbus_bank_base; 57*4249Sschwartz } fire4u_specific_t; 58*4249Sschwartz 59*4249Sschwartz typedef struct fire_counter_handle_impl { 60*4249Sschwartz ldi_handle_t devhandle; 61*4249Sschwartz fire4u_specific_t *devspec; /* Points to proper one for specific dev. */ 62*4249Sschwartz } fire_counter_handle_impl_t; 63*4249Sschwartz 64*4249Sschwartz static uint64_t counter_select_offsets[] = { 65*4249Sschwartz JBC_PERFORMANCE_COUNTER_SELECT, 66*4249Sschwartz IMU_PERFORMANCE_COUNTER_SELECT, 67*4249Sschwartz MMU_PERFORMANCE_COUNTER_SELECT, 68*4249Sschwartz TLU_PERFORMANCE_COUNTER_SELECT, 69*4249Sschwartz LPU_LINK_PERFORMANCE_COUNTER_SELECT 70*4249Sschwartz }; 71*4249Sschwartz 72*4249Sschwartz /* 73*4249Sschwartz * The following event and offset arrays is organized by grouping in major 74*4249Sschwartz * order the fire_perfcnt_t register types, and in minor order the register 75*4249Sschwartz * numbers within that type. 76*4249Sschwartz */ 77*4249Sschwartz 78*4249Sschwartz static uint64_t counter_reg_offsets[] = { 79*4249Sschwartz JBC_PERFORMANCE_COUNTER_ZERO, 80*4249Sschwartz JBC_PERFORMANCE_COUNTER_ONE, 81*4249Sschwartz IMU_PERFORMANCE_COUNTER_ZERO, 82*4249Sschwartz IMU_PERFORMANCE_COUNTER_ONE, 83*4249Sschwartz MMU_PERFORMANCE_COUNTER_ZERO, 84*4249Sschwartz MMU_PERFORMANCE_COUNTER_ONE, 85*4249Sschwartz TLU_PERFORMANCE_COUNTER_ZERO, 86*4249Sschwartz TLU_PERFORMANCE_COUNTER_ONE, 87*4249Sschwartz TLU_PERFORMANCE_COUNTER_TWO, 88*4249Sschwartz LPU_LINK_PERFORMANCE_COUNTER1, 89*4249Sschwartz LPU_LINK_PERFORMANCE_COUNTER2 90*4249Sschwartz }; 91*4249Sschwartz 92*4249Sschwartz /* 93*4249Sschwartz * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to 94*4249Sschwartz * write a value to that counter. 95*4249Sschwartz */ 96*4249Sschwartz #define LPU_LINK_PERFCTR_WRITE_OFFSET 0x8 97*4249Sschwartz 98*4249Sschwartz /* 99*4249Sschwartz * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to 100*4249Sschwartz * zeros and this is the value we want. This register isn't touched by this 101*4249Sschwartz * module, and as long as it remains untouched by other modules we're OK. 102*4249Sschwartz */ 103*4249Sschwartz 104*4249Sschwartz static ldi_ident_t ldi_identifier; 105*4249Sschwartz static boolean_t ldi_identifier_valid = B_FALSE; 106*4249Sschwartz static cred_t *credentials = NULL; 107*4249Sschwartz 108*4249Sschwartz /* Called by _init to determine if it is OK to install driver. */ 109*4249Sschwartz int 110*4249Sschwartz fpc_platform_check() 111*4249Sschwartz { 112*4249Sschwartz return (SUCCESS); 113*4249Sschwartz } 114*4249Sschwartz 115*4249Sschwartz /* Called during attach to do module-wide initialization. */ 116*4249Sschwartz int 117*4249Sschwartz fpc_platform_module_init(dev_info_t *dip) 118*4249Sschwartz { 119*4249Sschwartz int status; 120*4249Sschwartz 121*4249Sschwartz credentials = crget(); 122*4249Sschwartz status = ldi_ident_from_dip(dip, &ldi_identifier); 123*4249Sschwartz if (status == 0) 124*4249Sschwartz ldi_identifier_valid = B_TRUE; 125*4249Sschwartz return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE); 126*4249Sschwartz } 127*4249Sschwartz 128*4249Sschwartz int 129*4249Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail) 130*4249Sschwartz { 131*4249Sschwartz int index; 132*4249Sschwartz char *name; 133*4249Sschwartz int nodename_size; 134*4249Sschwartz char *nodename = NULL; 135*4249Sschwartz fire4u_specific_t *platform_specific_data = NULL; 136*4249Sschwartz char *compatible = NULL; 137*4249Sschwartz px_regs_t *regs_p = NULL; 138*4249Sschwartz int regs_length = 0; 139*4249Sschwartz 140*4249Sschwartz if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 141*4249Sschwartz "compatible", &compatible) != DDI_PROP_SUCCESS) 142*4249Sschwartz return (DDI_SUCCESS); 143*4249Sschwartz 144*4249Sschwartz if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) { 145*4249Sschwartz ddi_prop_free(compatible); 146*4249Sschwartz return (DDI_SUCCESS); 147*4249Sschwartz } 148*4249Sschwartz ddi_prop_free(compatible); 149*4249Sschwartz 150*4249Sschwartz fpc_common_node_setup(dip, &index); 151*4249Sschwartz 152*4249Sschwartz name = fpc_get_dev_name_by_number(index); 153*4249Sschwartz nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2; 154*4249Sschwartz nodename = kmem_zalloc(nodename_size, KM_SLEEP); 155*4249Sschwartz 156*4249Sschwartz platform_specific_data = 157*4249Sschwartz kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP); 158*4249Sschwartz 159*4249Sschwartz (void) strcpy(nodename, name); 160*4249Sschwartz (void) strcat(nodename, ":"); 161*4249Sschwartz (void) strcat(nodename, PCI_MINOR_REG); 162*4249Sschwartz platform_specific_data->nodename = nodename; 163*4249Sschwartz 164*4249Sschwartz /* Get register banks. */ 165*4249Sschwartz if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 166*4249Sschwartz "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) { 167*4249Sschwartz goto bad_regs_p; 168*4249Sschwartz } 169*4249Sschwartz 170*4249Sschwartz if ((regs_length / sizeof (px_regs_t)) < 2) { 171*4249Sschwartz goto bad_regs_length; 172*4249Sschwartz } 173*4249Sschwartz 174*4249Sschwartz platform_specific_data->jbus_bank_base = 175*4249Sschwartz regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK; 176*4249Sschwartz 177*4249Sschwartz kmem_free(regs_p, regs_length); 178*4249Sschwartz 179*4249Sschwartz if (index == 0) 180*4249Sschwartz *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL); 181*4249Sschwartz else 182*4249Sschwartz *avail |= PCIE_B_REGS_AVAIL; 183*4249Sschwartz 184*4249Sschwartz (void) fpc_set_platform_data_by_number(index, platform_specific_data); 185*4249Sschwartz 186*4249Sschwartz return (DDI_SUCCESS); 187*4249Sschwartz 188*4249Sschwartz bad_regs_length: 189*4249Sschwartz if (regs_p) 190*4249Sschwartz kmem_free(regs_p, regs_length); 191*4249Sschwartz bad_regs_p: 192*4249Sschwartz if (platform_specific_data) 193*4249Sschwartz kmem_free(platform_specific_data, sizeof (fire4u_specific_t)); 194*4249Sschwartz if (nodename) 195*4249Sschwartz kmem_free(nodename, nodename_size); 196*4249Sschwartz 197*4249Sschwartz return (DDI_FAILURE); 198*4249Sschwartz } 199*4249Sschwartz 200*4249Sschwartz void 201*4249Sschwartz fpc_platform_node_fini(void *arg) 202*4249Sschwartz { 203*4249Sschwartz fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg; 204*4249Sschwartz if (plat_arg == NULL) 205*4249Sschwartz return; 206*4249Sschwartz if (plat_arg->nodename) 207*4249Sschwartz kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1); 208*4249Sschwartz kmem_free(plat_arg, sizeof (fire4u_specific_t)); 209*4249Sschwartz } 210*4249Sschwartz 211*4249Sschwartz /*ARGSUSED*/ 212*4249Sschwartz void 213*4249Sschwartz fpc_platform_module_fini(dev_info_t *dip) 214*4249Sschwartz { 215*4249Sschwartz if (ldi_identifier_valid) 216*4249Sschwartz ldi_ident_release(ldi_identifier); 217*4249Sschwartz if (credentials) 218*4249Sschwartz crfree(credentials); 219*4249Sschwartz } 220*4249Sschwartz 221*4249Sschwartz fire_perfreg_handle_t 222*4249Sschwartz fpc_get_perfreg_handle(int devnum) 223*4249Sschwartz { 224*4249Sschwartz int rval = EINVAL; 225*4249Sschwartz 226*4249Sschwartz fire_counter_handle_impl_t *handle_impl = 227*4249Sschwartz kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP); 228*4249Sschwartz 229*4249Sschwartz if ((handle_impl->devspec = 230*4249Sschwartz fpc_get_platform_data_by_number(devnum)) != NULL) { 231*4249Sschwartz rval = ldi_open_by_name(handle_impl->devspec->nodename, 232*4249Sschwartz OPEN_FLAGS, credentials, &handle_impl->devhandle, 233*4249Sschwartz ldi_identifier); 234*4249Sschwartz } 235*4249Sschwartz 236*4249Sschwartz if (rval != SUCCESS) { 237*4249Sschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t)); 238*4249Sschwartz return ((fire_perfreg_handle_t)-1); 239*4249Sschwartz } else { 240*4249Sschwartz return ((fire_perfreg_handle_t)handle_impl); 241*4249Sschwartz } 242*4249Sschwartz } 243*4249Sschwartz 244*4249Sschwartz int 245*4249Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle) 246*4249Sschwartz { 247*4249Sschwartz fire_counter_handle_impl_t *handle_impl = 248*4249Sschwartz (fire_counter_handle_impl_t *)handle; 249*4249Sschwartz (void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials); 250*4249Sschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t)); 251*4249Sschwartz return (SUCCESS); 252*4249Sschwartz } 253*4249Sschwartz 254*4249Sschwartz int 255*4249Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 256*4249Sschwartz uint64_t *reg_data, boolean_t is_write) 257*4249Sschwartz { 258*4249Sschwartz int rval; 259*4249Sschwartz int ioctl_rval; 260*4249Sschwartz pcitool_reg_t prg; 261*4249Sschwartz fire_counter_handle_impl_t *handle_impl = 262*4249Sschwartz (fire_counter_handle_impl_t *)handle; 263*4249Sschwartz int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; 264*4249Sschwartz 265*4249Sschwartz prg.user_version = PCITOOL_USER_VERSION; 266*4249Sschwartz 267*4249Sschwartz if (group == jbc) { 268*4249Sschwartz prg.barnum = JBUS_BANK; 269*4249Sschwartz prg.offset = counter_select_offsets[group] - 270*4249Sschwartz handle_impl->devspec->jbus_bank_base; 271*4249Sschwartz } else { 272*4249Sschwartz prg.barnum = PCIE_BANK; 273*4249Sschwartz 274*4249Sschwartz /* 275*4249Sschwartz * Note that a pcie_bank_base isn't needed. Pcie register 276*4249Sschwartz * offsets are already relative to the start of their bank. No 277*4249Sschwartz * base needs to be subtracted to get the relative offset that 278*4249Sschwartz * pcitool ioctls want. 279*4249Sschwartz */ 280*4249Sschwartz prg.offset = counter_select_offsets[group]; 281*4249Sschwartz } 282*4249Sschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG; 283*4249Sschwartz prg.data = *reg_data; 284*4249Sschwartz 285*4249Sschwartz /* Read original value. */ 286*4249Sschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg, 287*4249Sschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) { 288*4249Sschwartz *reg_data = prg.data; 289*4249Sschwartz } 290*4249Sschwartz 291*4249Sschwartz return (rval); 292*4249Sschwartz } 293*4249Sschwartz 294*4249Sschwartz int 295*4249Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 296*4249Sschwartz int counter_index, uint64_t *value, boolean_t is_write) 297*4249Sschwartz { 298*4249Sschwartz int rval; 299*4249Sschwartz int ioctl_rval; 300*4249Sschwartz pcitool_reg_t prg; 301*4249Sschwartz fire_counter_handle_impl_t *handle_impl = 302*4249Sschwartz (fire_counter_handle_impl_t *)handle; 303*4249Sschwartz int command = 304*4249Sschwartz (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; 305*4249Sschwartz 306*4249Sschwartz prg.user_version = PCITOOL_USER_VERSION; 307*4249Sschwartz /* 308*4249Sschwartz * Note that stated PCIE offsets are relative to the beginning of their 309*4249Sschwartz * register bank, while JBUS offsets are absolute. 310*4249Sschwartz */ 311*4249Sschwartz if (group == jbc) { 312*4249Sschwartz prg.barnum = JBUS_BANK; 313*4249Sschwartz prg.offset = counter_reg_offsets[counter_index] - 314*4249Sschwartz handle_impl->devspec->jbus_bank_base; 315*4249Sschwartz } else { 316*4249Sschwartz prg.barnum = PCIE_BANK; 317*4249Sschwartz prg.offset = counter_reg_offsets[counter_index]; 318*4249Sschwartz } 319*4249Sschwartz 320*4249Sschwartz if ((group == lpu) && (is_write)) { 321*4249Sschwartz prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET; 322*4249Sschwartz } 323*4249Sschwartz 324*4249Sschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG; 325*4249Sschwartz prg.data = *value; 326*4249Sschwartz 327*4249Sschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg, 328*4249Sschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) { 329*4249Sschwartz *value = prg.data; 330*4249Sschwartz } 331*4249Sschwartz 332*4249Sschwartz return (rval); 333*4249Sschwartz } 334