11370Sschwartz /*
21370Sschwartz * CDDL HEADER START
31370Sschwartz *
41370Sschwartz * The contents of this file are subject to the terms of the
51370Sschwartz * Common Development and Distribution License (the "License").
61370Sschwartz * You may not use this file except in compliance with the License.
71370Sschwartz *
81370Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91370Sschwartz * or http://www.opensolaris.org/os/licensing.
101370Sschwartz * See the License for the specific language governing permissions
111370Sschwartz * and limitations under the License.
121370Sschwartz *
131370Sschwartz * When distributing Covered Code, include this CDDL HEADER in each
141370Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151370Sschwartz * If applicable, add the following below this CDDL HEADER, with the
161370Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying
171370Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner]
181370Sschwartz *
191370Sschwartz * CDDL HEADER END
201370Sschwartz */
211370Sschwartz
221370Sschwartz /*
231370Sschwartz * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
241370Sschwartz * Use is subject to license terms.
251370Sschwartz */
261370Sschwartz
271370Sschwartz #pragma ident "%Z%%M% %I% %E% SMI"
281370Sschwartz
291370Sschwartz #include <sys/file.h>
301370Sschwartz #include <sys/hypervisor_api.h>
31*1691Sschwartz #include <sys/hsvc.h>
321370Sschwartz #include <sys/sunndi.h>
331370Sschwartz #include <fpc.h>
341370Sschwartz #include <fpc-impl.h>
351370Sschwartz #include <fpc-impl-4v.h>
361370Sschwartz
371370Sschwartz #define PCIE_ROOTNEX_COMPATIBLE_NAME "SUNW,sun4v-pci"
381370Sschwartz
39*1691Sschwartz #define FPC_MODULE_NAME "fpc"
40*1691Sschwartz #define FPC_REQ_MAJOR_VER 1
41*1691Sschwartz #define FPC_REQ_MINOR_VER 0
42*1691Sschwartz
43*1691Sschwartz static hsvc_info_t fpc_hsvc = {
44*1691Sschwartz HSVC_REV_1,
45*1691Sschwartz NULL,
46*1691Sschwartz HSVC_GROUP_FIRE_PERF,
47*1691Sschwartz FPC_REQ_MAJOR_VER,
48*1691Sschwartz FPC_REQ_MINOR_VER,
49*1691Sschwartz FPC_MODULE_NAME
50*1691Sschwartz };
51*1691Sschwartz
52*1691Sschwartz static int hyp_regd_users = 0;
53*1691Sschwartz static uint64_t fpc_sup_minor;
54*1691Sschwartz
551370Sschwartz /*
561370Sschwartz * The following typedef is used to represent a
571370Sschwartz * 1275 "reg" property of a PCI nexus.
581370Sschwartz */
591370Sschwartz typedef struct nexus_regspec {
601370Sschwartz uint64_t phys_addr;
611370Sschwartz uint64_t size;
621370Sschwartz } nexus_regspec_t;
631370Sschwartz
641370Sschwartz static uint64_t counter_select_index[] = {
651370Sschwartz HVIO_FIRE_PERFREG_JBC_SEL,
661370Sschwartz HVIO_FIRE_PERFREG_PCIE_IMU_SEL,
671370Sschwartz HVIO_FIRE_PERFREG_PCIE_MMU_SEL,
681370Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_SEL,
691370Sschwartz HVIO_FIRE_PERFREG_PCIE_LNK_SEL
701370Sschwartz };
711370Sschwartz
721370Sschwartz /*
731370Sschwartz * The following event and offset arrays is organized by grouping in major
741370Sschwartz * order the fire_perfcnt_t register types, and in minor order the register
751370Sschwartz * numbers within that type.
761370Sschwartz */
771370Sschwartz
781370Sschwartz /*
791370Sschwartz * This table maps the above order into the hypervisor interface register
801370Sschwartz * indices.
811370Sschwartz */
821370Sschwartz static uint64_t counter_reg_index[] = {
831370Sschwartz HVIO_FIRE_PERFREG_JBC_CNT0,
841370Sschwartz HVIO_FIRE_PERFREG_JBC_CNT1,
851370Sschwartz HVIO_FIRE_PERFREG_PCIE_IMU_CNT0,
861370Sschwartz HVIO_FIRE_PERFREG_PCIE_IMU_CNT1,
871370Sschwartz HVIO_FIRE_PERFREG_PCIE_MMU_CNT0,
881370Sschwartz HVIO_FIRE_PERFREG_PCIE_MMU_CNT1,
891370Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_CNT0,
901370Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_CNT1,
911370Sschwartz HVIO_FIRE_PERFREG_PCIE_TLU_CNT2,
921370Sschwartz HVIO_FIRE_PERFREG_PCIE_LNK_CNT1,
931370Sschwartz HVIO_FIRE_PERFREG_PCIE_LNK_CNT2
941370Sschwartz };
951370Sschwartz
96*1691Sschwartz /* Called by _init to determine if it is OK to install driver. */
97*1691Sschwartz int
fpc_platform_check()98*1691Sschwartz fpc_platform_check()
99*1691Sschwartz {
100*1691Sschwartz int regstat;
101*1691Sschwartz
102*1691Sschwartz if ((regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor)) == SUCCESS) {
103*1691Sschwartz (void) hsvc_unregister(&fpc_hsvc);
104*1691Sschwartz }
105*1691Sschwartz fpc_sup_minor = 0;
106*1691Sschwartz return (regstat);
107*1691Sschwartz }
108*1691Sschwartz
109*1691Sschwartz /* Called during attach to do module-wide initialization. */
1101370Sschwartz /*ARGSUSED*/
1111370Sschwartz int
fpc_platform_module_init(dev_info_t * dip)1121370Sschwartz fpc_platform_module_init(dev_info_t *dip)
1131370Sschwartz {
1141370Sschwartz return (DDI_SUCCESS);
1151370Sschwartz }
1161370Sschwartz
1171370Sschwartz int
fpc_platform_node_init(dev_info_t * dip,int * avail)1181370Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail)
1191370Sschwartz {
1201370Sschwartz nexus_regspec_t *rp;
1211370Sschwartz uint_t reglen;
1221370Sschwartz devhandle_t dev_hdl;
123*1691Sschwartz int regstat;
1241370Sschwartz int index;
1251370Sschwartz boolean_t is_root_pcie_nexus;
1261370Sschwartz uint64_t dummy_data;
1271370Sschwartz char *name = NULL;
1281370Sschwartz boolean_t jbus_regs_avail;
1291370Sschwartz boolean_t pcie_regs_avail;
1301370Sschwartz
1311370Sschwartz if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
1321370Sschwartz DDI_PROP_DONTPASS, "compatible", &name) != DDI_PROP_SUCCESS)
1331370Sschwartz return (DDI_SUCCESS);
1341370Sschwartz
1351370Sschwartz is_root_pcie_nexus = (strcmp(name, PCIE_ROOTNEX_COMPATIBLE_NAME) == 0);
1361370Sschwartz ddi_prop_free(name);
1371370Sschwartz if (!is_root_pcie_nexus)
1381370Sschwartz return (DDI_SUCCESS);
1391370Sschwartz
1401370Sschwartz if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
1411370Sschwartz DDI_PROP_DONTPASS, "reg", (uchar_t **)&rp, ®len) !=
1421370Sschwartz DDI_PROP_SUCCESS)
1431370Sschwartz return (DDI_FAILURE);
1441370Sschwartz
1451370Sschwartz /*
1461370Sschwartz * Initilize device handle. The device handle uniquely
1471370Sschwartz * identifies a SUN4V device. It consists of the lower 28-bits
1481370Sschwartz * of the hi-cell of the first entry of the SUN4V device's
1491370Sschwartz * "reg" property as defined by the SUN4V Bus Binding to Open
1501370Sschwartz * Firmware.
1511370Sschwartz */
1521370Sschwartz dev_hdl = (devhandle_t)((rp->phys_addr >> 32) & DEVHDLE_MASK);
1531370Sschwartz
1541370Sschwartz ddi_prop_free(rp);
1551370Sschwartz
156*1691Sschwartz /*
157*1691Sschwartz * If this is the first time through here, negotiate with hypervisor
158*1691Sschwartz * that it has the services needed to operate. Don't do this in _init
159*1691Sschwartz * since we may want to modload the driver without attaching,
160*1691Sschwartz * for debugging purposes.
161*1691Sschwartz *
162*1691Sschwartz * Note that this is another way of weeding out unsupported platforms
163*1691Sschwartz */
164*1691Sschwartz if (hyp_regd_users == 0) {
165*1691Sschwartz regstat = hsvc_register(&fpc_hsvc, &fpc_sup_minor);
166*1691Sschwartz if (regstat != SUCCESS) {
167*1691Sschwartz /*
168*1691Sschwartz * Fail silently since we don't want to print an error
169*1691Sschwartz * on future platforms which don't support this driver.
170*1691Sschwartz */
171*1691Sschwartz return (DDI_FAILURE);
172*1691Sschwartz }
173*1691Sschwartz }
174*1691Sschwartz hyp_regd_users++;
175*1691Sschwartz
1761370Sschwartz /* See which register sets are usable from this node. */
1771370Sschwartz jbus_regs_avail = (fpc_event_io(
1781370Sschwartz (fire_perfreg_handle_t)dev_hdl, jbc, &dummy_data, IS_READ) ==
1791370Sschwartz SUCCESS);
1801370Sschwartz pcie_regs_avail = (fpc_event_io(
1811370Sschwartz (fire_perfreg_handle_t)dev_hdl, imu, &dummy_data, IS_READ) ==
1821370Sschwartz SUCCESS);
1831370Sschwartz
1841370Sschwartz /* Nothing usable at this node. */
1851370Sschwartz if ((!jbus_regs_avail) && (!pcie_regs_avail))
1861370Sschwartz return (DDI_SUCCESS);
1871370Sschwartz
1881370Sschwartz fpc_common_node_setup(dip, &index);
1891370Sschwartz if (pcie_regs_avail)
1901370Sschwartz *avail |=
1911370Sschwartz ((index == 0) ? PCIE_A_REGS_AVAIL : PCIE_B_REGS_AVAIL);
1921370Sschwartz if (jbus_regs_avail) {
1931370Sschwartz *avail |= JBUS_REGS_AVAIL;
1941370Sschwartz if (index != 0)
1951370Sschwartz cmn_err(CE_WARN,
1961370Sschwartz "fpc: JBUS regs available on device idx %d!\n",
1971370Sschwartz index);
1981370Sschwartz }
1991370Sschwartz
2001370Sschwartz (void) fpc_set_platform_data_by_number(index, (void *)dev_hdl);
2011370Sschwartz
2021370Sschwartz return (DDI_SUCCESS);
2031370Sschwartz }
2041370Sschwartz
2051370Sschwartz /*ARGSUSED*/
2061370Sschwartz void
fpc_platform_node_fini(void * arg)2071370Sschwartz fpc_platform_node_fini(void *arg)
2081370Sschwartz {
209*1691Sschwartz if (--hyp_regd_users == 0)
210*1691Sschwartz (void) hsvc_unregister(&fpc_hsvc);
2111370Sschwartz }
2121370Sschwartz
2131370Sschwartz /*ARGSUSED*/
2141370Sschwartz void
fpc_platform_module_fini(dev_info_t * dip)2151370Sschwartz fpc_platform_module_fini(dev_info_t *dip)
2161370Sschwartz {
2171370Sschwartz }
2181370Sschwartz
2191370Sschwartz fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)2201370Sschwartz fpc_get_perfreg_handle(int devnum)
2211370Sschwartz {
2221370Sschwartz void *platform_specific_data;
2231370Sschwartz
2241370Sschwartz if ((platform_specific_data =
2251370Sschwartz fpc_get_platform_data_by_number(devnum)) == NULL)
2261370Sschwartz return ((fire_perfreg_handle_t)-1);
2271370Sschwartz else
2281370Sschwartz return ((fire_perfreg_handle_t)platform_specific_data);
2291370Sschwartz }
2301370Sschwartz
2311370Sschwartz /*ARGSUSED*/
2321370Sschwartz int
fpc_free_counter_handle(fire_perfreg_handle_t handle)2331370Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle)
2341370Sschwartz {
2351370Sschwartz return (SUCCESS);
2361370Sschwartz }
2371370Sschwartz
2381370Sschwartz static int
fpc_hv_perfreg_io(fire_perfreg_handle_t handle,uint64_t hv_if_index,uint64_t * reg_data,boolean_t is_write)2391370Sschwartz fpc_hv_perfreg_io(fire_perfreg_handle_t handle, uint64_t hv_if_index,
2401370Sschwartz uint64_t *reg_data, boolean_t is_write)
2411370Sschwartz {
2421370Sschwartz int rval;
2431370Sschwartz devhandle_t dev_hdl = (devhandle_t)handle;
2441370Sschwartz
2451370Sschwartz if (is_write)
2461370Sschwartz rval = fpc_set_fire_perfreg(dev_hdl, hv_if_index, *reg_data);
2471370Sschwartz else
2481370Sschwartz rval = fpc_get_fire_perfreg(dev_hdl, hv_if_index, reg_data);
2491370Sschwartz
2501370Sschwartz return ((rval == H_EOK) ? SUCCESS : EIO);
2511370Sschwartz }
2521370Sschwartz
2531370Sschwartz int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)2541370Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2551370Sschwartz uint64_t *reg_data, boolean_t is_write)
2561370Sschwartz {
2571370Sschwartz uint64_t hv_if_index = counter_select_index[group];
2581370Sschwartz return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
2591370Sschwartz }
2601370Sschwartz
2611370Sschwartz
2621370Sschwartz /*ARGSUSED*/
2631370Sschwartz int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * reg_data,boolean_t is_write)2641370Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2651370Sschwartz int counter_index, uint64_t *reg_data, boolean_t is_write)
2661370Sschwartz {
2671370Sschwartz uint64_t hv_if_index = counter_reg_index[counter_index];
2681370Sschwartz return (fpc_hv_perfreg_io(handle, hv_if_index, reg_data, is_write));
2691370Sschwartz }
270