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