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 /* 232917a9c9Sschwartz * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24110e73f9Sschwartz * Use is subject to license terms. 25110e73f9Sschwartz */ 26110e73f9Sschwartz 27110e73f9Sschwartz #include <sys/file.h> 28110e73f9Sschwartz #include <sys/sunndi.h> 29110e73f9Sschwartz #include <sys/sunddi.h> 30110e73f9Sschwartz #include <sys/sunldi.h> 31110e73f9Sschwartz #include <io/px/px_regs.h> 32110e73f9Sschwartz #include <sys/pci_tools.h> 33110e73f9Sschwartz #include <fpc.h> 34110e73f9Sschwartz #include <fpc-impl.h> 35110e73f9Sschwartz 36110e73f9Sschwartz #define CHIP_COMPATIBLE_NAME "pciex108e,80f0" 37110e73f9Sschwartz #define BANK_ADDR_MASK 0x7FFFFF 38110e73f9Sschwartz 39110e73f9Sschwartz #define OPEN_FLAGS (FREAD | FWRITE) 40110e73f9Sschwartz 41110e73f9Sschwartz #define PCIE_BANK 0 42110e73f9Sschwartz #define JBUS_BANK 1 43110e73f9Sschwartz 44110e73f9Sschwartz typedef struct px_regs { 45110e73f9Sschwartz uint32_t addr_hi; 46110e73f9Sschwartz uint32_t addr_lo; 47110e73f9Sschwartz uint32_t size_hi; 48110e73f9Sschwartz uint32_t size_lo; 49110e73f9Sschwartz } px_regs_t; 50110e73f9Sschwartz 51110e73f9Sschwartz /* There is one of these for every root nexus device found */ 52110e73f9Sschwartz typedef struct fire4u_specific { 53110e73f9Sschwartz char *nodename; 54110e73f9Sschwartz uintptr_t jbus_bank_base; 55110e73f9Sschwartz } fire4u_specific_t; 56110e73f9Sschwartz 57110e73f9Sschwartz typedef struct fire_counter_handle_impl { 58110e73f9Sschwartz ldi_handle_t devhandle; 59110e73f9Sschwartz fire4u_specific_t *devspec; /* Points to proper one for specific dev. */ 60110e73f9Sschwartz } fire_counter_handle_impl_t; 61110e73f9Sschwartz 62110e73f9Sschwartz static uint64_t counter_select_offsets[] = { 63110e73f9Sschwartz JBC_PERFORMANCE_COUNTER_SELECT, 64110e73f9Sschwartz IMU_PERFORMANCE_COUNTER_SELECT, 65110e73f9Sschwartz MMU_PERFORMANCE_COUNTER_SELECT, 66110e73f9Sschwartz TLU_PERFORMANCE_COUNTER_SELECT, 67110e73f9Sschwartz LPU_LINK_PERFORMANCE_COUNTER_SELECT 68110e73f9Sschwartz }; 69110e73f9Sschwartz 70110e73f9Sschwartz /* 71110e73f9Sschwartz * The following event and offset arrays is organized by grouping in major 72110e73f9Sschwartz * order the fire_perfcnt_t register types, and in minor order the register 73110e73f9Sschwartz * numbers within that type. 74110e73f9Sschwartz */ 75110e73f9Sschwartz 76110e73f9Sschwartz static uint64_t counter_reg_offsets[] = { 77110e73f9Sschwartz JBC_PERFORMANCE_COUNTER_ZERO, 78110e73f9Sschwartz JBC_PERFORMANCE_COUNTER_ONE, 79110e73f9Sschwartz IMU_PERFORMANCE_COUNTER_ZERO, 80110e73f9Sschwartz IMU_PERFORMANCE_COUNTER_ONE, 81110e73f9Sschwartz MMU_PERFORMANCE_COUNTER_ZERO, 82110e73f9Sschwartz MMU_PERFORMANCE_COUNTER_ONE, 83110e73f9Sschwartz TLU_PERFORMANCE_COUNTER_ZERO, 84110e73f9Sschwartz TLU_PERFORMANCE_COUNTER_ONE, 85110e73f9Sschwartz TLU_PERFORMANCE_COUNTER_TWO, 86110e73f9Sschwartz LPU_LINK_PERFORMANCE_COUNTER1, 87110e73f9Sschwartz LPU_LINK_PERFORMANCE_COUNTER2 88110e73f9Sschwartz }; 89110e73f9Sschwartz 90110e73f9Sschwartz /* 91110e73f9Sschwartz * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to 92110e73f9Sschwartz * write a value to that counter. 93110e73f9Sschwartz */ 94110e73f9Sschwartz #define LPU_LINK_PERFCTR_WRITE_OFFSET 0x8 95110e73f9Sschwartz 96110e73f9Sschwartz /* 97110e73f9Sschwartz * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to 98110e73f9Sschwartz * zeros and this is the value we want. This register isn't touched by this 99110e73f9Sschwartz * module, and as long as it remains untouched by other modules we're OK. 100110e73f9Sschwartz */ 101110e73f9Sschwartz 102110e73f9Sschwartz static ldi_ident_t ldi_identifier; 103110e73f9Sschwartz static boolean_t ldi_identifier_valid = B_FALSE; 104110e73f9Sschwartz static cred_t *credentials = NULL; 105110e73f9Sschwartz 1060ad689d6Sschwartz /* Called by _init to determine if it is OK to install driver. */ 1070ad689d6Sschwartz int 1080ad689d6Sschwartz fpc_platform_check() 1090ad689d6Sschwartz { 1100ad689d6Sschwartz return (SUCCESS); 1110ad689d6Sschwartz } 1120ad689d6Sschwartz 1130ad689d6Sschwartz /* Called during attach to do module-wide initialization. */ 114110e73f9Sschwartz int 115110e73f9Sschwartz fpc_platform_module_init(dev_info_t *dip) 116110e73f9Sschwartz { 117110e73f9Sschwartz int status; 118110e73f9Sschwartz 119110e73f9Sschwartz credentials = crget(); 120110e73f9Sschwartz status = ldi_ident_from_dip(dip, &ldi_identifier); 121110e73f9Sschwartz if (status == 0) 122110e73f9Sschwartz ldi_identifier_valid = B_TRUE; 123110e73f9Sschwartz return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE); 124110e73f9Sschwartz } 125110e73f9Sschwartz 126110e73f9Sschwartz int 127110e73f9Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail) 128110e73f9Sschwartz { 129110e73f9Sschwartz int index; 130110e73f9Sschwartz char *name; 131110e73f9Sschwartz int nodename_size; 132110e73f9Sschwartz char *nodename = NULL; 133110e73f9Sschwartz fire4u_specific_t *platform_specific_data = NULL; 134110e73f9Sschwartz char *compatible = NULL; 135110e73f9Sschwartz px_regs_t *regs_p = NULL; 136110e73f9Sschwartz int regs_length = 0; 137110e73f9Sschwartz 138110e73f9Sschwartz if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 139110e73f9Sschwartz "compatible", &compatible) != DDI_PROP_SUCCESS) 140110e73f9Sschwartz return (DDI_SUCCESS); 141110e73f9Sschwartz 142110e73f9Sschwartz if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) { 143110e73f9Sschwartz ddi_prop_free(compatible); 144110e73f9Sschwartz return (DDI_SUCCESS); 145110e73f9Sschwartz } 146110e73f9Sschwartz ddi_prop_free(compatible); 147110e73f9Sschwartz 148110e73f9Sschwartz fpc_common_node_setup(dip, &index); 149110e73f9Sschwartz 150110e73f9Sschwartz name = fpc_get_dev_name_by_number(index); 151110e73f9Sschwartz nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2; 152110e73f9Sschwartz nodename = kmem_zalloc(nodename_size, KM_SLEEP); 153110e73f9Sschwartz 154110e73f9Sschwartz platform_specific_data = 1552917a9c9Sschwartz kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP); 156110e73f9Sschwartz 157110e73f9Sschwartz (void) strcpy(nodename, name); 158110e73f9Sschwartz (void) strcat(nodename, ":"); 159110e73f9Sschwartz (void) strcat(nodename, PCI_MINOR_REG); 160110e73f9Sschwartz platform_specific_data->nodename = nodename; 161110e73f9Sschwartz 162110e73f9Sschwartz /* Get register banks. */ 163110e73f9Sschwartz if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1642917a9c9Sschwartz "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) { 165110e73f9Sschwartz goto bad_regs_p; 166110e73f9Sschwartz } 167110e73f9Sschwartz 168110e73f9Sschwartz if ((regs_length / sizeof (px_regs_t)) < 2) { 169110e73f9Sschwartz goto bad_regs_length; 170110e73f9Sschwartz } 171110e73f9Sschwartz 172110e73f9Sschwartz platform_specific_data->jbus_bank_base = 173110e73f9Sschwartz regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK; 174110e73f9Sschwartz 175110e73f9Sschwartz kmem_free(regs_p, regs_length); 176110e73f9Sschwartz 177110e73f9Sschwartz if (index == 0) 178110e73f9Sschwartz *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL); 179110e73f9Sschwartz else 180110e73f9Sschwartz *avail |= PCIE_B_REGS_AVAIL; 181110e73f9Sschwartz 182110e73f9Sschwartz (void) fpc_set_platform_data_by_number(index, platform_specific_data); 183110e73f9Sschwartz 184110e73f9Sschwartz return (DDI_SUCCESS); 185110e73f9Sschwartz 186110e73f9Sschwartz bad_regs_length: 187110e73f9Sschwartz if (regs_p) 188110e73f9Sschwartz kmem_free(regs_p, regs_length); 189110e73f9Sschwartz bad_regs_p: 190*c5fab18eSToomas Soome kmem_free(platform_specific_data, sizeof (fire4u_specific_t)); 191110e73f9Sschwartz if (nodename) 192110e73f9Sschwartz kmem_free(nodename, nodename_size); 193110e73f9Sschwartz 194110e73f9Sschwartz return (DDI_FAILURE); 195110e73f9Sschwartz } 196110e73f9Sschwartz 197110e73f9Sschwartz void 198110e73f9Sschwartz fpc_platform_node_fini(void *arg) 199110e73f9Sschwartz { 200110e73f9Sschwartz fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg; 201110e73f9Sschwartz if (plat_arg == NULL) 202110e73f9Sschwartz return; 203110e73f9Sschwartz if (plat_arg->nodename) 204110e73f9Sschwartz kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1); 205110e73f9Sschwartz kmem_free(plat_arg, sizeof (fire4u_specific_t)); 206110e73f9Sschwartz } 207110e73f9Sschwartz 208110e73f9Sschwartz /*ARGSUSED*/ 209110e73f9Sschwartz void 210110e73f9Sschwartz fpc_platform_module_fini(dev_info_t *dip) 211110e73f9Sschwartz { 212110e73f9Sschwartz if (ldi_identifier_valid) 213110e73f9Sschwartz ldi_ident_release(ldi_identifier); 214110e73f9Sschwartz if (credentials) 215110e73f9Sschwartz crfree(credentials); 216110e73f9Sschwartz } 217110e73f9Sschwartz 218110e73f9Sschwartz fire_perfreg_handle_t 219110e73f9Sschwartz fpc_get_perfreg_handle(int devnum) 220110e73f9Sschwartz { 221110e73f9Sschwartz int rval = EINVAL; 222110e73f9Sschwartz 223110e73f9Sschwartz fire_counter_handle_impl_t *handle_impl = 224110e73f9Sschwartz kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP); 225110e73f9Sschwartz 226110e73f9Sschwartz if ((handle_impl->devspec = 227110e73f9Sschwartz fpc_get_platform_data_by_number(devnum)) != NULL) { 228110e73f9Sschwartz rval = ldi_open_by_name(handle_impl->devspec->nodename, 229110e73f9Sschwartz OPEN_FLAGS, credentials, &handle_impl->devhandle, 230110e73f9Sschwartz ldi_identifier); 231110e73f9Sschwartz } 232110e73f9Sschwartz 233110e73f9Sschwartz if (rval != SUCCESS) { 234110e73f9Sschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t)); 235110e73f9Sschwartz return ((fire_perfreg_handle_t)-1); 236110e73f9Sschwartz } else { 237110e73f9Sschwartz return ((fire_perfreg_handle_t)handle_impl); 238110e73f9Sschwartz } 239110e73f9Sschwartz } 240110e73f9Sschwartz 241110e73f9Sschwartz int 242110e73f9Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle) 243110e73f9Sschwartz { 244110e73f9Sschwartz fire_counter_handle_impl_t *handle_impl = 245110e73f9Sschwartz (fire_counter_handle_impl_t *)handle; 246110e73f9Sschwartz (void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials); 247110e73f9Sschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t)); 248110e73f9Sschwartz return (SUCCESS); 249110e73f9Sschwartz } 250110e73f9Sschwartz 251110e73f9Sschwartz int 252110e73f9Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 253110e73f9Sschwartz uint64_t *reg_data, boolean_t is_write) 254110e73f9Sschwartz { 255110e73f9Sschwartz int rval; 256110e73f9Sschwartz int ioctl_rval; 257110e73f9Sschwartz pcitool_reg_t prg; 258110e73f9Sschwartz fire_counter_handle_impl_t *handle_impl = 259110e73f9Sschwartz (fire_counter_handle_impl_t *)handle; 260110e73f9Sschwartz int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; 261110e73f9Sschwartz 2622917a9c9Sschwartz prg.user_version = PCITOOL_VERSION; 263110e73f9Sschwartz 264110e73f9Sschwartz if (group == jbc) { 265110e73f9Sschwartz prg.barnum = JBUS_BANK; 266110e73f9Sschwartz prg.offset = counter_select_offsets[group] - 267110e73f9Sschwartz handle_impl->devspec->jbus_bank_base; 268110e73f9Sschwartz } else { 269110e73f9Sschwartz prg.barnum = PCIE_BANK; 270110e73f9Sschwartz 271110e73f9Sschwartz /* 272110e73f9Sschwartz * Note that a pcie_bank_base isn't needed. Pcie register 273110e73f9Sschwartz * offsets are already relative to the start of their bank. No 274110e73f9Sschwartz * base needs to be subtracted to get the relative offset that 275110e73f9Sschwartz * pcitool ioctls want. 276110e73f9Sschwartz */ 277110e73f9Sschwartz prg.offset = counter_select_offsets[group]; 278110e73f9Sschwartz } 279110e73f9Sschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG; 280110e73f9Sschwartz prg.data = *reg_data; 281110e73f9Sschwartz 282110e73f9Sschwartz /* Read original value. */ 283110e73f9Sschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg, 284110e73f9Sschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) { 285110e73f9Sschwartz *reg_data = prg.data; 286110e73f9Sschwartz } 287110e73f9Sschwartz 288110e73f9Sschwartz return (rval); 289110e73f9Sschwartz } 290110e73f9Sschwartz 291110e73f9Sschwartz int 292110e73f9Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 293110e73f9Sschwartz int counter_index, uint64_t *value, boolean_t is_write) 294110e73f9Sschwartz { 295110e73f9Sschwartz int rval; 296110e73f9Sschwartz int ioctl_rval; 297110e73f9Sschwartz pcitool_reg_t prg; 298110e73f9Sschwartz fire_counter_handle_impl_t *handle_impl = 299110e73f9Sschwartz (fire_counter_handle_impl_t *)handle; 300110e73f9Sschwartz int command = 301110e73f9Sschwartz (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; 302110e73f9Sschwartz 3032917a9c9Sschwartz prg.user_version = PCITOOL_VERSION; 304110e73f9Sschwartz /* 305110e73f9Sschwartz * Note that stated PCIE offsets are relative to the beginning of their 306110e73f9Sschwartz * register bank, while JBUS offsets are absolute. 307110e73f9Sschwartz */ 308110e73f9Sschwartz if (group == jbc) { 309110e73f9Sschwartz prg.barnum = JBUS_BANK; 310110e73f9Sschwartz prg.offset = counter_reg_offsets[counter_index] - 311110e73f9Sschwartz handle_impl->devspec->jbus_bank_base; 312110e73f9Sschwartz } else { 313110e73f9Sschwartz prg.barnum = PCIE_BANK; 314110e73f9Sschwartz prg.offset = counter_reg_offsets[counter_index]; 315110e73f9Sschwartz } 316110e73f9Sschwartz 317110e73f9Sschwartz if ((group == lpu) && (is_write)) { 318110e73f9Sschwartz prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET; 319110e73f9Sschwartz } 320110e73f9Sschwartz 321110e73f9Sschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG; 322110e73f9Sschwartz prg.data = *value; 323110e73f9Sschwartz 324110e73f9Sschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg, 325110e73f9Sschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) { 326110e73f9Sschwartz *value = prg.data; 327110e73f9Sschwartz } 328110e73f9Sschwartz 329110e73f9Sschwartz return (rval); 330110e73f9Sschwartz } 331