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/file.h> 30110e73f9Sschwartz #include <sys/hypervisor_api.h> 31*0ad689d6Sschwartz #include <sys/hsvc.h> 32110e73f9Sschwartz #include <sys/sunndi.h> 33110e73f9Sschwartz #include <fpc.h> 34110e73f9Sschwartz #include <fpc-impl.h> 35110e73f9Sschwartz #include <fpc-impl-4v.h> 36110e73f9Sschwartz 37110e73f9Sschwartz #define PCIE_ROOTNEX_COMPATIBLE_NAME "SUNW,sun4v-pci" 38110e73f9Sschwartz 39*0ad689d6Sschwartz #define FPC_MODULE_NAME "fpc" 40*0ad689d6Sschwartz #define FPC_REQ_MAJOR_VER 1 41*0ad689d6Sschwartz #define FPC_REQ_MINOR_VER 0 42*0ad689d6Sschwartz 43*0ad689d6Sschwartz static hsvc_info_t fpc_hsvc = { 44*0ad689d6Sschwartz HSVC_REV_1, 45*0ad689d6Sschwartz NULL, 46*0ad689d6Sschwartz HSVC_GROUP_FIRE_PERF, 47*0ad689d6Sschwartz FPC_REQ_MAJOR_VER, 48*0ad689d6Sschwartz FPC_REQ_MINOR_VER, 49*0ad689d6Sschwartz FPC_MODULE_NAME 50*0ad689d6Sschwartz }; 51*0ad689d6Sschwartz 52*0ad689d6Sschwartz static int hyp_regd_users = 0; 53*0ad689d6Sschwartz static uint64_t fpc_sup_minor; 54*0ad689d6Sschwartz 55110e73f9Sschwartz /* 56110e73f9Sschwartz * The following typedef is used to represent a 57110e73f9Sschwartz * 1275 "reg" property of a PCI nexus. 58110e73f9Sschwartz */ 59110e73f9Sschwartz typedef struct nexus_regspec { 60110e73f9Sschwartz uint64_t phys_addr; 61110e73f9Sschwartz uint64_t size; 62110e73f9Sschwartz } nexus_regspec_t; 63110e73f9Sschwartz 64110e73f9Sschwartz static uint64_t counter_select_index[] = { 65110e73f9Sschwartz HVIO_FIRE_PERFREG_JBC_SEL, 66110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_IMU_SEL, 67110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_MMU_SEL, 68110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_SEL, 69110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_LNK_SEL 70110e73f9Sschwartz }; 71110e73f9Sschwartz 72110e73f9Sschwartz /* 73110e73f9Sschwartz * The following event and offset arrays is organized by grouping in major 74110e73f9Sschwartz * order the fire_perfcnt_t register types, and in minor order the register 75110e73f9Sschwartz * numbers within that type. 76110e73f9Sschwartz */ 77110e73f9Sschwartz 78110e73f9Sschwartz /* 79110e73f9Sschwartz * This table maps the above order into the hypervisor interface register 80110e73f9Sschwartz * indices. 81110e73f9Sschwartz */ 82110e73f9Sschwartz static uint64_t counter_reg_index[] = { 83110e73f9Sschwartz HVIO_FIRE_PERFREG_JBC_CNT0, 84110e73f9Sschwartz HVIO_FIRE_PERFREG_JBC_CNT1, 85110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_IMU_CNT0, 86110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_IMU_CNT1, 87110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_MMU_CNT0, 88110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_MMU_CNT1, 89110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_CNT0, 90110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_CNT1, 91110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_CNT2, 92110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_LNK_CNT1, 93110e73f9Sschwartz HVIO_FIRE_PERFREG_PCIE_LNK_CNT2 94110e73f9Sschwartz }; 95110e73f9Sschwartz 96*0ad689d6Sschwartz /* Called by _init to determine if it is OK to install driver. */ 97*0ad689d6Sschwartz int 98*0ad689d6Sschwartz fpc_platform_check() 99*0ad689d6Sschwartz { 100*0ad689d6Sschwartz int regstat; 101*0ad689d6Sschwartz 102*0ad689d6Sschwartz if ((regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor)) == SUCCESS) { 103*0ad689d6Sschwartz (void) hsvc_unregister(&fpc_hsvc); 104*0ad689d6Sschwartz } 105*0ad689d6Sschwartz fpc_sup_minor = 0; 106*0ad689d6Sschwartz return (regstat); 107*0ad689d6Sschwartz } 108*0ad689d6Sschwartz 109*0ad689d6Sschwartz /* Called during attach to do module-wide initialization. */ 110110e73f9Sschwartz /*ARGSUSED*/ 111110e73f9Sschwartz int 112110e73f9Sschwartz fpc_platform_module_init(dev_info_t *dip) 113110e73f9Sschwartz { 114110e73f9Sschwartz return (DDI_SUCCESS); 115110e73f9Sschwartz } 116110e73f9Sschwartz 117110e73f9Sschwartz int 118110e73f9Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail) 119110e73f9Sschwartz { 120110e73f9Sschwartz nexus_regspec_t *rp; 121110e73f9Sschwartz uint_t reglen; 122110e73f9Sschwartz devhandle_t dev_hdl; 123*0ad689d6Sschwartz int regstat; 124110e73f9Sschwartz int index; 125110e73f9Sschwartz boolean_t is_root_pcie_nexus; 126110e73f9Sschwartz uint64_t dummy_data; 127110e73f9Sschwartz char *name = NULL; 128110e73f9Sschwartz boolean_t jbus_regs_avail; 129110e73f9Sschwartz boolean_t pcie_regs_avail; 130110e73f9Sschwartz 131110e73f9Sschwartz if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 132110e73f9Sschwartz DDI_PROP_DONTPASS, "compatible", &name) != DDI_PROP_SUCCESS) 133110e73f9Sschwartz return (DDI_SUCCESS); 134110e73f9Sschwartz 135110e73f9Sschwartz is_root_pcie_nexus = (strcmp(name, PCIE_ROOTNEX_COMPATIBLE_NAME) == 0); 136110e73f9Sschwartz ddi_prop_free(name); 137110e73f9Sschwartz if (!is_root_pcie_nexus) 138110e73f9Sschwartz return (DDI_SUCCESS); 139110e73f9Sschwartz 140110e73f9Sschwartz if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, 141110e73f9Sschwartz DDI_PROP_DONTPASS, "reg", (uchar_t **)&rp, ®len) != 142110e73f9Sschwartz DDI_PROP_SUCCESS) 143110e73f9Sschwartz return (DDI_FAILURE); 144110e73f9Sschwartz 145110e73f9Sschwartz /* 146110e73f9Sschwartz * Initilize device handle. The device handle uniquely 147110e73f9Sschwartz * identifies a SUN4V device. It consists of the lower 28-bits 148110e73f9Sschwartz * of the hi-cell of the first entry of the SUN4V device's 149110e73f9Sschwartz * "reg" property as defined by the SUN4V Bus Binding to Open 150110e73f9Sschwartz * Firmware. 151110e73f9Sschwartz */ 152110e73f9Sschwartz dev_hdl = (devhandle_t)((rp->phys_addr >> 32) & DEVHDLE_MASK); 153110e73f9Sschwartz 154110e73f9Sschwartz ddi_prop_free(rp); 155110e73f9Sschwartz 156*0ad689d6Sschwartz /* 157*0ad689d6Sschwartz * If this is the first time through here, negotiate with hypervisor 158*0ad689d6Sschwartz * that it has the services needed to operate. Don't do this in _init 159*0ad689d6Sschwartz * since we may want to modload the driver without attaching, 160*0ad689d6Sschwartz * for debugging purposes. 161*0ad689d6Sschwartz * 162*0ad689d6Sschwartz * Note that this is another way of weeding out unsupported platforms 163*0ad689d6Sschwartz */ 164*0ad689d6Sschwartz if (hyp_regd_users == 0) { 165*0ad689d6Sschwartz regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor); 166*0ad689d6Sschwartz if (regstat != SUCCESS) { 167*0ad689d6Sschwartz /* 168*0ad689d6Sschwartz * Fail silently since we don't want to print an error 169*0ad689d6Sschwartz * on future platforms which don't support this driver. 170*0ad689d6Sschwartz */ 171*0ad689d6Sschwartz return (DDI_FAILURE); 172*0ad689d6Sschwartz } 173*0ad689d6Sschwartz } 174*0ad689d6Sschwartz hyp_regd_users++; 175*0ad689d6Sschwartz 176110e73f9Sschwartz /* See which register sets are usable from this node. */ 177110e73f9Sschwartz jbus_regs_avail = (fpc_event_io( 178110e73f9Sschwartz (fire_perfreg_handle_t)dev_hdl, jbc, &dummy_data, IS_READ) == 179110e73f9Sschwartz SUCCESS); 180110e73f9Sschwartz pcie_regs_avail = (fpc_event_io( 181110e73f9Sschwartz (fire_perfreg_handle_t)dev_hdl, imu, &dummy_data, IS_READ) == 182110e73f9Sschwartz SUCCESS); 183110e73f9Sschwartz 184110e73f9Sschwartz /* Nothing usable at this node. */ 185110e73f9Sschwartz if ((!jbus_regs_avail) && (!pcie_regs_avail)) 186110e73f9Sschwartz return (DDI_SUCCESS); 187110e73f9Sschwartz 188110e73f9Sschwartz fpc_common_node_setup(dip, &index); 189110e73f9Sschwartz if (pcie_regs_avail) 190110e73f9Sschwartz *avail |= 191110e73f9Sschwartz ((index == 0) ? PCIE_A_REGS_AVAIL : PCIE_B_REGS_AVAIL); 192110e73f9Sschwartz if (jbus_regs_avail) { 193110e73f9Sschwartz *avail |= JBUS_REGS_AVAIL; 194110e73f9Sschwartz if (index != 0) 195110e73f9Sschwartz cmn_err(CE_WARN, 196110e73f9Sschwartz "fpc: JBUS regs available on device idx %d!\n", 197110e73f9Sschwartz index); 198110e73f9Sschwartz } 199110e73f9Sschwartz 200110e73f9Sschwartz (void) fpc_set_platform_data_by_number(index, (void *)dev_hdl); 201110e73f9Sschwartz 202110e73f9Sschwartz return (DDI_SUCCESS); 203110e73f9Sschwartz } 204110e73f9Sschwartz 205110e73f9Sschwartz /*ARGSUSED*/ 206110e73f9Sschwartz void 207110e73f9Sschwartz fpc_platform_node_fini(void *arg) 208110e73f9Sschwartz { 209*0ad689d6Sschwartz if (--hyp_regd_users == 0) 210*0ad689d6Sschwartz (void) hsvc_unregister(&fpc_hsvc); 211110e73f9Sschwartz } 212110e73f9Sschwartz 213110e73f9Sschwartz /*ARGSUSED*/ 214110e73f9Sschwartz void 215110e73f9Sschwartz fpc_platform_module_fini(dev_info_t *dip) 216110e73f9Sschwartz { 217110e73f9Sschwartz } 218110e73f9Sschwartz 219110e73f9Sschwartz fire_perfreg_handle_t 220110e73f9Sschwartz fpc_get_perfreg_handle(int devnum) 221110e73f9Sschwartz { 222110e73f9Sschwartz void *platform_specific_data; 223110e73f9Sschwartz 224110e73f9Sschwartz if ((platform_specific_data = 225110e73f9Sschwartz fpc_get_platform_data_by_number(devnum)) == NULL) 226110e73f9Sschwartz return ((fire_perfreg_handle_t)-1); 227110e73f9Sschwartz else 228110e73f9Sschwartz return ((fire_perfreg_handle_t)platform_specific_data); 229110e73f9Sschwartz } 230110e73f9Sschwartz 231110e73f9Sschwartz /*ARGSUSED*/ 232110e73f9Sschwartz int 233110e73f9Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle) 234110e73f9Sschwartz { 235110e73f9Sschwartz return (SUCCESS); 236110e73f9Sschwartz } 237110e73f9Sschwartz 238110e73f9Sschwartz static int 239110e73f9Sschwartz fpc_hv_perfreg_io(fire_perfreg_handle_t handle, uint64_t hv_if_index, 240110e73f9Sschwartz uint64_t *reg_data, boolean_t is_write) 241110e73f9Sschwartz { 242110e73f9Sschwartz int rval; 243110e73f9Sschwartz devhandle_t dev_hdl = (devhandle_t)handle; 244110e73f9Sschwartz 245110e73f9Sschwartz if (is_write) 246110e73f9Sschwartz rval = fpc_set_fire_perfreg(dev_hdl, hv_if_index, *reg_data); 247110e73f9Sschwartz else 248110e73f9Sschwartz rval = fpc_get_fire_perfreg(dev_hdl, hv_if_index, reg_data); 249110e73f9Sschwartz 250110e73f9Sschwartz return ((rval == H_EOK) ? SUCCESS : EIO); 251110e73f9Sschwartz } 252110e73f9Sschwartz 253110e73f9Sschwartz int 254110e73f9Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 255110e73f9Sschwartz uint64_t *reg_data, boolean_t is_write) 256110e73f9Sschwartz { 257110e73f9Sschwartz uint64_t hv_if_index = counter_select_index[group]; 258110e73f9Sschwartz return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write)); 259110e73f9Sschwartz } 260110e73f9Sschwartz 261110e73f9Sschwartz 262110e73f9Sschwartz /*ARGSUSED*/ 263110e73f9Sschwartz int 264110e73f9Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group, 265110e73f9Sschwartz int counter_index, uint64_t *reg_data, boolean_t is_write) 266110e73f9Sschwartz { 267110e73f9Sschwartz uint64_t hv_if_index = counter_reg_index[counter_index]; 268110e73f9Sschwartz return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write)); 269110e73f9Sschwartz } 270