16528affbSschwartz /* 26528affbSschwartz * CDDL HEADER START 36528affbSschwartz * 46528affbSschwartz * The contents of this file are subject to the terms of the 56528affbSschwartz * Common Development and Distribution License (the "License"). 66528affbSschwartz * You may not use this file except in compliance with the License. 76528affbSschwartz * 86528affbSschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96528affbSschwartz * or http://www.opensolaris.org/os/licensing. 106528affbSschwartz * See the License for the specific language governing permissions 116528affbSschwartz * and limitations under the License. 126528affbSschwartz * 136528affbSschwartz * When distributing Covered Code, include this CDDL HEADER in each 146528affbSschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156528affbSschwartz * If applicable, add the following below this CDDL HEADER, with the 166528affbSschwartz * fields enclosed by brackets "[]" replaced with your own identifying 176528affbSschwartz * information: Portions Copyright [yyyy] [name of copyright owner] 186528affbSschwartz * 196528affbSschwartz * CDDL HEADER END 206528affbSschwartz */ 216528affbSschwartz 226528affbSschwartz /* 23*2917a9c9Sschwartz * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 246528affbSschwartz * Use is subject to license terms. 256528affbSschwartz */ 266528affbSschwartz 276528affbSschwartz #pragma ident "%Z%%M% %I% %E% SMI" 286528affbSschwartz 296528affbSschwartz #include <sys/file.h> 306528affbSschwartz #include <sys/sunndi.h> 316528affbSschwartz #include <sys/sunddi.h> 326528affbSschwartz #include <sys/sunldi.h> 336528affbSschwartz #include <io/px/px_regs.h> 346528affbSschwartz #include <sys/pci_tools.h> 356528affbSschwartz #include <fpc.h> 366528affbSschwartz #include <fpc-impl.h> 376528affbSschwartz 386528affbSschwartz #define CHIP_COMPATIBLE_NAME "pciex108e,80f0" 396528affbSschwartz #define BANK_ADDR_MASK 0x7FFFFF 406528affbSschwartz 416528affbSschwartz #define OPEN_FLAGS (FREAD | FWRITE) 426528affbSschwartz 436528affbSschwartz #define PCIE_BANK 0 446528affbSschwartz #define JBUS_BANK 1 456528affbSschwartz 466528affbSschwartz typedef struct px_regs { 476528affbSschwartz uint32_t addr_hi; 486528affbSschwartz uint32_t addr_lo; 496528affbSschwartz uint32_t size_hi; 506528affbSschwartz uint32_t size_lo; 516528affbSschwartz } px_regs_t; 526528affbSschwartz 536528affbSschwartz /* There is one of these for every root nexus device found */ 546528affbSschwartz typedef struct fire4u_specific { 556528affbSschwartz char *nodename; 566528affbSschwartz uintptr_t jbus_bank_base; 576528affbSschwartz } fire4u_specific_t; 586528affbSschwartz 596528affbSschwartz typedef struct fire_counter_handle_impl { 606528affbSschwartz ldi_handle_t devhandle; 616528affbSschwartz fire4u_specific_t *devspec; /* Points to proper one for specific dev. */ 626528affbSschwartz } fire_counter_handle_impl_t; 636528affbSschwartz 646528affbSschwartz static uint64_t counter_select_offsets[] = { 656528affbSschwartz JBC_PERFORMANCE_COUNTER_SELECT, 666528affbSschwartz IMU_PERFORMANCE_COUNTER_SELECT, 676528affbSschwartz MMU_PERFORMANCE_COUNTER_SELECT, 686528affbSschwartz TLU_PERFORMANCE_COUNTER_SELECT, 696528affbSschwartz LPU_LINK_PERFORMANCE_COUNTER_SELECT 706528affbSschwartz }; 716528affbSschwartz 726528affbSschwartz /* 736528affbSschwartz * The following event and offset arrays is organized by grouping in major 746528affbSschwartz * order the fire_perfcnt_t register types, and in minor order the register 756528affbSschwartz * numbers within that type. 766528affbSschwartz */ 776528affbSschwartz 786528affbSschwartz static uint64_t counter_reg_offsets[] = { 796528affbSschwartz JBC_PERFORMANCE_COUNTER_ZERO, 806528affbSschwartz JBC_PERFORMANCE_COUNTER_ONE, 816528affbSschwartz IMU_PERFORMANCE_COUNTER_ZERO, 826528affbSschwartz IMU_PERFORMANCE_COUNTER_ONE, 836528affbSschwartz MMU_PERFORMANCE_COUNTER_ZERO, 846528affbSschwartz MMU_PERFORMANCE_COUNTER_ONE, 856528affbSschwartz TLU_PERFORMANCE_COUNTER_ZERO, 866528affbSschwartz TLU_PERFORMANCE_COUNTER_ONE, 876528affbSschwartz TLU_PERFORMANCE_COUNTER_TWO, 886528affbSschwartz LPU_LINK_PERFORMANCE_COUNTER1, 896528affbSschwartz LPU_LINK_PERFORMANCE_COUNTER2 906528affbSschwartz }; 916528affbSschwartz 926528affbSschwartz /* 936528affbSschwartz * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to 946528affbSschwartz * write a value to that counter. 956528affbSschwartz */ 966528affbSschwartz #define LPU_LINK_PERFCTR_WRITE_OFFSET 0x8 976528affbSschwartz 986528affbSschwartz /* 996528affbSschwartz * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to 1006528affbSschwartz * zeros and this is the value we want. This register isn't touched by this 1016528affbSschwartz * module, and as long as it remains untouched by other modules we're OK. 1026528affbSschwartz */ 1036528affbSschwartz 1046528affbSschwartz static ldi_ident_t ldi_identifier; 1056528affbSschwartz static boolean_t ldi_identifier_valid = B_FALSE; 1066528affbSschwartz static cred_t *credentials = NULL; 1076528affbSschwartz 1086528affbSschwartz /* Called by _init to determine if it is OK to install driver. */ 1096528affbSschwartz int 1106528affbSschwartz fpc_platform_check() 1116528affbSschwartz { 1126528affbSschwartz return (SUCCESS); 1136528affbSschwartz } 1146528affbSschwartz 1156528affbSschwartz /* Called during attach to do module-wide initialization. */ 1166528affbSschwartz int 1176528affbSschwartz fpc_platform_module_init(dev_info_t *dip) 1186528affbSschwartz { 1196528affbSschwartz int status; 1206528affbSschwartz 1216528affbSschwartz credentials = crget(); 1226528affbSschwartz status = ldi_ident_from_dip(dip, &ldi_identifier); 1236528affbSschwartz if (status == 0) 1246528affbSschwartz ldi_identifier_valid = B_TRUE; 1256528affbSschwartz return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE); 1266528affbSschwartz } 1276528affbSschwartz 1286528affbSschwartz int 1296528affbSschwartz fpc_platform_node_init(dev_info_t *dip, int *avail) 1306528affbSschwartz { 1316528affbSschwartz int index; 1326528affbSschwartz char *name; 1336528affbSschwartz int nodename_size; 1346528affbSschwartz char *nodename = NULL; 1356528affbSschwartz fire4u_specific_t *platform_specific_data = NULL; 1366528affbSschwartz char *compatible = NULL; 1376528affbSschwartz px_regs_t *regs_p = NULL; 1386528affbSschwartz int regs_length = 0; 1396528affbSschwartz 1406528affbSschwartz if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1416528affbSschwartz "compatible", &compatible) != DDI_PROP_SUCCESS) 1426528affbSschwartz return (DDI_SUCCESS); 1436528affbSschwartz 1446528affbSschwartz if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) { 1456528affbSschwartz ddi_prop_free(compatible); 1466528affbSschwartz return (DDI_SUCCESS); 1476528affbSschwartz } 1486528affbSschwartz ddi_prop_free(compatible); 1496528affbSschwartz 1506528affbSschwartz fpc_common_node_setup(dip, &index); 1516528affbSschwartz 1526528affbSschwartz name = fpc_get_dev_name_by_number(index); 1536528affbSschwartz nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2; 1546528affbSschwartz nodename = kmem_zalloc(nodename_size, KM_SLEEP); 1556528affbSschwartz 1566528affbSschwartz platform_specific_data = 1576528affbSschwartz kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP); 1586528affbSschwartz 1596528affbSschwartz (void) strcpy(nodename, name); 1606528affbSschwartz (void) strcat(nodename, ":"); 1616528affbSschwartz (void) strcat(nodename, PCI_MINOR_REG); 1626528affbSschwartz platform_specific_data->nodename = nodename; 1636528affbSschwartz 1646528affbSschwartz /* Get register banks. */ 1656528affbSschwartz if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1666528affbSschwartz "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) { 1676528affbSschwartz goto bad_regs_p; 1686528affbSschwartz } 1696528affbSschwartz 1706528affbSschwartz if ((regs_length / sizeof (px_regs_t)) < 2) { 1716528affbSschwartz goto bad_regs_length; 1726528affbSschwartz } 1736528affbSschwartz 1746528affbSschwartz platform_specific_data->jbus_bank_base = 1756528affbSschwartz regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK; 1766528affbSschwartz 1776528affbSschwartz kmem_free(regs_p, regs_length); 1786528affbSschwartz 1796528affbSschwartz if (index == 0) 1806528affbSschwartz *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL); 1816528affbSschwartz else 1826528affbSschwartz *avail |= PCIE_B_REGS_AVAIL; 1836528affbSschwartz 1846528affbSschwartz (void) fpc_set_platform_data_by_number(index, platform_specific_data); 1856528affbSschwartz 1866528affbSschwartz return (DDI_SUCCESS); 1876528affbSschwartz 1886528affbSschwartz bad_regs_length: 1896528affbSschwartz if (regs_p) 1906528affbSschwartz kmem_free(regs_p, regs_length); 1916528affbSschwartz bad_regs_p: 1926528affbSschwartz if (platform_specific_data) 1936528affbSschwartz kmem_free(platform_specific_data, sizeof (fire4u_specific_t)); 1946528affbSschwartz if (nodename) 1956528affbSschwartz kmem_free(nodename, nodename_size); 1966528affbSschwartz 1976528affbSschwartz return (DDI_FAILURE); 1986528affbSschwartz } 1996528affbSschwartz 2006528affbSschwartz void 2016528affbSschwartz fpc_platform_node_fini(void *arg) 2026528affbSschwartz { 2036528affbSschwartz fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg; 2046528affbSschwartz if (plat_arg == NULL) 2056528affbSschwartz return; 2066528affbSschwartz if (plat_arg->nodename) 2076528affbSschwartz kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1); 2086528affbSschwartz kmem_free(plat_arg, sizeof (fire4u_specific_t)); 2096528affbSschwartz } 2106528affbSschwartz 2116528affbSschwartz /*ARGSUSED*/ 2126528affbSschwartz void 2136528affbSschwartz fpc_platform_module_fini(dev_info_t *dip) 2146528affbSschwartz { 2156528affbSschwartz if (ldi_identifier_valid) 2166528affbSschwartz ldi_ident_release(ldi_identifier); 2176528affbSschwartz if (credentials) 2186528affbSschwartz crfree(credentials); 2196528affbSschwartz } 2206528affbSschwartz 2216528affbSschwartz fire_perfreg_handle_t 2226528affbSschwartz fpc_get_perfreg_handle(int devnum) 2236528affbSschwartz { 2246528affbSschwartz int rval = EINVAL; 2256528affbSschwartz 2266528affbSschwartz fire_counter_handle_impl_t *handle_impl = 2276528affbSschwartz kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP); 2286528affbSschwartz 2296528affbSschwartz if ((handle_impl->devspec = 2306528affbSschwartz fpc_get_platform_data_by_number(devnum)) != NULL) { 2316528affbSschwartz rval = ldi_open_by_name(handle_impl->devspec->nodename, 2326528affbSschwartz OPEN_FLAGS, credentials, &handle_impl->devhandle, 2336528affbSschwartz ldi_identifier); 2346528affbSschwartz } 2356528affbSschwartz 2366528affbSschwartz if (rval != SUCCESS) { 2376528affbSschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t)); 2386528affbSschwartz return ((fire_perfreg_handle_t)-1); 2396528affbSschwartz } else { 2406528affbSschwartz return ((fire_perfreg_handle_t)handle_impl); 2416528affbSschwartz } 2426528affbSschwartz } 2436528affbSschwartz 2446528affbSschwartz int 2456528affbSschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle) 2466528affbSschwartz { 2476528affbSschwartz fire_counter_handle_impl_t *handle_impl = 2486528affbSschwartz (fire_counter_handle_impl_t *)handle; 2496528affbSschwartz (void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials); 2506528affbSschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t)); 2516528affbSschwartz return (SUCCESS); 2526528affbSschwartz } 2536528affbSschwartz 2546528affbSschwartz int 2556528affbSschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 2566528affbSschwartz uint64_t *reg_data, boolean_t is_write) 2576528affbSschwartz { 2586528affbSschwartz int rval; 2596528affbSschwartz int ioctl_rval; 2606528affbSschwartz pcitool_reg_t prg; 2616528affbSschwartz fire_counter_handle_impl_t *handle_impl = 2626528affbSschwartz (fire_counter_handle_impl_t *)handle; 2636528affbSschwartz int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; 2646528affbSschwartz 265*2917a9c9Sschwartz prg.user_version = PCITOOL_VERSION; 2666528affbSschwartz 2676528affbSschwartz if (group == jbc) { 2686528affbSschwartz prg.barnum = JBUS_BANK; 2696528affbSschwartz prg.offset = counter_select_offsets[group] - 2706528affbSschwartz handle_impl->devspec->jbus_bank_base; 2716528affbSschwartz } else { 2726528affbSschwartz prg.barnum = PCIE_BANK; 2736528affbSschwartz 2746528affbSschwartz /* 2756528affbSschwartz * Note that a pcie_bank_base isn't needed. Pcie register 2766528affbSschwartz * offsets are already relative to the start of their bank. No 2776528affbSschwartz * base needs to be subtracted to get the relative offset that 2786528affbSschwartz * pcitool ioctls want. 2796528affbSschwartz */ 2806528affbSschwartz prg.offset = counter_select_offsets[group]; 2816528affbSschwartz } 2826528affbSschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG; 2836528affbSschwartz prg.data = *reg_data; 2846528affbSschwartz 2856528affbSschwartz /* Read original value. */ 2866528affbSschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg, 2876528affbSschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) { 2886528affbSschwartz *reg_data = prg.data; 2896528affbSschwartz } 2906528affbSschwartz 2916528affbSschwartz return (rval); 2926528affbSschwartz } 2936528affbSschwartz 2946528affbSschwartz int 2956528affbSschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 2966528affbSschwartz int counter_index, uint64_t *value, boolean_t is_write) 2976528affbSschwartz { 2986528affbSschwartz int rval; 2996528affbSschwartz int ioctl_rval; 3006528affbSschwartz pcitool_reg_t prg; 3016528affbSschwartz fire_counter_handle_impl_t *handle_impl = 3026528affbSschwartz (fire_counter_handle_impl_t *)handle; 3036528affbSschwartz int command = 3046528affbSschwartz (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG; 3056528affbSschwartz 306*2917a9c9Sschwartz prg.user_version = PCITOOL_VERSION; 3076528affbSschwartz /* 3086528affbSschwartz * Note that stated PCIE offsets are relative to the beginning of their 3096528affbSschwartz * register bank, while JBUS offsets are absolute. 3106528affbSschwartz */ 3116528affbSschwartz if (group == jbc) { 3126528affbSschwartz prg.barnum = JBUS_BANK; 3136528affbSschwartz prg.offset = counter_reg_offsets[counter_index] - 3146528affbSschwartz handle_impl->devspec->jbus_bank_base; 3156528affbSschwartz } else { 3166528affbSschwartz prg.barnum = PCIE_BANK; 3176528affbSschwartz prg.offset = counter_reg_offsets[counter_index]; 3186528affbSschwartz } 3196528affbSschwartz 3206528affbSschwartz if ((group == lpu) && (is_write)) { 3216528affbSschwartz prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET; 3226528affbSschwartz } 3236528affbSschwartz 3246528affbSschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG; 3256528affbSschwartz prg.data = *value; 3266528affbSschwartz 3276528affbSschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg, 3286528affbSschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) { 3296528affbSschwartz *value = prg.data; 3306528affbSschwartz } 3316528affbSschwartz 3326528affbSschwartz return (rval); 3336528affbSschwartz } 334