14249Sschwartz /*
24249Sschwartz * CDDL HEADER START
34249Sschwartz *
44249Sschwartz * The contents of this file are subject to the terms of the
54249Sschwartz * Common Development and Distribution License (the "License").
64249Sschwartz * You may not use this file except in compliance with the License.
74249Sschwartz *
84249Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94249Sschwartz * or http://www.opensolaris.org/os/licensing.
104249Sschwartz * See the License for the specific language governing permissions
114249Sschwartz * and limitations under the License.
124249Sschwartz *
134249Sschwartz * When distributing Covered Code, include this CDDL HEADER in each
144249Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154249Sschwartz * If applicable, add the following below this CDDL HEADER, with the
164249Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying
174249Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner]
184249Sschwartz *
194249Sschwartz * CDDL HEADER END
204249Sschwartz */
214249Sschwartz
224249Sschwartz /*
23*4397Sschwartz * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
244249Sschwartz * Use is subject to license terms.
254249Sschwartz */
264249Sschwartz
274249Sschwartz #pragma ident "%Z%%M% %I% %E% SMI"
284249Sschwartz
294249Sschwartz #include <sys/file.h>
304249Sschwartz #include <sys/sunndi.h>
314249Sschwartz #include <sys/sunddi.h>
324249Sschwartz #include <sys/sunldi.h>
334249Sschwartz #include <io/px/px_regs.h>
344249Sschwartz #include <sys/pci_tools.h>
354249Sschwartz #include <fpc.h>
364249Sschwartz #include <fpc-impl.h>
374249Sschwartz
384249Sschwartz #define CHIP_COMPATIBLE_NAME "pciex108e,80f0"
394249Sschwartz #define BANK_ADDR_MASK 0x7FFFFF
404249Sschwartz
414249Sschwartz #define OPEN_FLAGS (FREAD | FWRITE)
424249Sschwartz
434249Sschwartz #define PCIE_BANK 0
444249Sschwartz #define JBUS_BANK 1
454249Sschwartz
464249Sschwartz typedef struct px_regs {
474249Sschwartz uint32_t addr_hi;
484249Sschwartz uint32_t addr_lo;
494249Sschwartz uint32_t size_hi;
504249Sschwartz uint32_t size_lo;
514249Sschwartz } px_regs_t;
524249Sschwartz
534249Sschwartz /* There is one of these for every root nexus device found */
544249Sschwartz typedef struct fire4u_specific {
554249Sschwartz char *nodename;
564249Sschwartz uintptr_t jbus_bank_base;
574249Sschwartz } fire4u_specific_t;
584249Sschwartz
594249Sschwartz typedef struct fire_counter_handle_impl {
604249Sschwartz ldi_handle_t devhandle;
614249Sschwartz fire4u_specific_t *devspec; /* Points to proper one for specific dev. */
624249Sschwartz } fire_counter_handle_impl_t;
634249Sschwartz
644249Sschwartz static uint64_t counter_select_offsets[] = {
654249Sschwartz JBC_PERFORMANCE_COUNTER_SELECT,
664249Sschwartz IMU_PERFORMANCE_COUNTER_SELECT,
674249Sschwartz MMU_PERFORMANCE_COUNTER_SELECT,
684249Sschwartz TLU_PERFORMANCE_COUNTER_SELECT,
694249Sschwartz LPU_LINK_PERFORMANCE_COUNTER_SELECT
704249Sschwartz };
714249Sschwartz
724249Sschwartz /*
734249Sschwartz * The following event and offset arrays is organized by grouping in major
744249Sschwartz * order the fire_perfcnt_t register types, and in minor order the register
754249Sschwartz * numbers within that type.
764249Sschwartz */
774249Sschwartz
784249Sschwartz static uint64_t counter_reg_offsets[] = {
794249Sschwartz JBC_PERFORMANCE_COUNTER_ZERO,
804249Sschwartz JBC_PERFORMANCE_COUNTER_ONE,
814249Sschwartz IMU_PERFORMANCE_COUNTER_ZERO,
824249Sschwartz IMU_PERFORMANCE_COUNTER_ONE,
834249Sschwartz MMU_PERFORMANCE_COUNTER_ZERO,
844249Sschwartz MMU_PERFORMANCE_COUNTER_ONE,
854249Sschwartz TLU_PERFORMANCE_COUNTER_ZERO,
864249Sschwartz TLU_PERFORMANCE_COUNTER_ONE,
874249Sschwartz TLU_PERFORMANCE_COUNTER_TWO,
884249Sschwartz LPU_LINK_PERFORMANCE_COUNTER1,
894249Sschwartz LPU_LINK_PERFORMANCE_COUNTER2
904249Sschwartz };
914249Sschwartz
924249Sschwartz /*
934249Sschwartz * Add the following to one of the LPU_LINK_PERFORMANCE_COUNTERx offsets to
944249Sschwartz * write a value to that counter.
954249Sschwartz */
964249Sschwartz #define LPU_LINK_PERFCTR_WRITE_OFFSET 0x8
974249Sschwartz
984249Sschwartz /*
994249Sschwartz * Note that LPU_LINK_PERFORMANCE_COUNTER_CONTROL register is hard-reset to
1004249Sschwartz * zeros and this is the value we want. This register isn't touched by this
1014249Sschwartz * module, and as long as it remains untouched by other modules we're OK.
1024249Sschwartz */
1034249Sschwartz
1044249Sschwartz static ldi_ident_t ldi_identifier;
1054249Sschwartz static boolean_t ldi_identifier_valid = B_FALSE;
1064249Sschwartz static cred_t *credentials = NULL;
1074249Sschwartz
1084249Sschwartz /* Called by _init to determine if it is OK to install driver. */
1094249Sschwartz int
fpc_platform_check()1104249Sschwartz fpc_platform_check()
1114249Sschwartz {
1124249Sschwartz return (SUCCESS);
1134249Sschwartz }
1144249Sschwartz
1154249Sschwartz /* Called during attach to do module-wide initialization. */
1164249Sschwartz int
fpc_platform_module_init(dev_info_t * dip)1174249Sschwartz fpc_platform_module_init(dev_info_t *dip)
1184249Sschwartz {
1194249Sschwartz int status;
1204249Sschwartz
1214249Sschwartz credentials = crget();
1224249Sschwartz status = ldi_ident_from_dip(dip, &ldi_identifier);
1234249Sschwartz if (status == 0)
1244249Sschwartz ldi_identifier_valid = B_TRUE;
1254249Sschwartz return ((status == 0) ? DDI_SUCCESS : DDI_FAILURE);
1264249Sschwartz }
1274249Sschwartz
1284249Sschwartz int
fpc_platform_node_init(dev_info_t * dip,int * avail)1294249Sschwartz fpc_platform_node_init(dev_info_t *dip, int *avail)
1304249Sschwartz {
1314249Sschwartz int index;
1324249Sschwartz char *name;
1334249Sschwartz int nodename_size;
1344249Sschwartz char *nodename = NULL;
1354249Sschwartz fire4u_specific_t *platform_specific_data = NULL;
1364249Sschwartz char *compatible = NULL;
1374249Sschwartz px_regs_t *regs_p = NULL;
1384249Sschwartz int regs_length = 0;
1394249Sschwartz
1404249Sschwartz if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1414249Sschwartz "compatible", &compatible) != DDI_PROP_SUCCESS)
1424249Sschwartz return (DDI_SUCCESS);
1434249Sschwartz
1444249Sschwartz if (strcmp(compatible, CHIP_COMPATIBLE_NAME) != 0) {
1454249Sschwartz ddi_prop_free(compatible);
1464249Sschwartz return (DDI_SUCCESS);
1474249Sschwartz }
1484249Sschwartz ddi_prop_free(compatible);
1494249Sschwartz
1504249Sschwartz fpc_common_node_setup(dip, &index);
1514249Sschwartz
1524249Sschwartz name = fpc_get_dev_name_by_number(index);
1534249Sschwartz nodename_size = strlen(name) + strlen(PCI_MINOR_REG) + 2;
1544249Sschwartz nodename = kmem_zalloc(nodename_size, KM_SLEEP);
1554249Sschwartz
1564249Sschwartz platform_specific_data =
157*4397Sschwartz kmem_zalloc(sizeof (fire4u_specific_t), KM_SLEEP);
1584249Sschwartz
1594249Sschwartz (void) strcpy(nodename, name);
1604249Sschwartz (void) strcat(nodename, ":");
1614249Sschwartz (void) strcat(nodename, PCI_MINOR_REG);
1624249Sschwartz platform_specific_data->nodename = nodename;
1634249Sschwartz
1644249Sschwartz /* Get register banks. */
1654249Sschwartz if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
166*4397Sschwartz "reg", (caddr_t)®s_p, ®s_length) != DDI_SUCCESS) {
1674249Sschwartz goto bad_regs_p;
1684249Sschwartz }
1694249Sschwartz
1704249Sschwartz if ((regs_length / sizeof (px_regs_t)) < 2) {
1714249Sschwartz goto bad_regs_length;
1724249Sschwartz }
1734249Sschwartz
1744249Sschwartz platform_specific_data->jbus_bank_base =
1754249Sschwartz regs_p[JBUS_BANK].addr_lo & BANK_ADDR_MASK;
1764249Sschwartz
1774249Sschwartz kmem_free(regs_p, regs_length);
1784249Sschwartz
1794249Sschwartz if (index == 0)
1804249Sschwartz *avail |= (PCIE_A_REGS_AVAIL | JBUS_REGS_AVAIL);
1814249Sschwartz else
1824249Sschwartz *avail |= PCIE_B_REGS_AVAIL;
1834249Sschwartz
1844249Sschwartz (void) fpc_set_platform_data_by_number(index, platform_specific_data);
1854249Sschwartz
1864249Sschwartz return (DDI_SUCCESS);
1874249Sschwartz
1884249Sschwartz bad_regs_length:
1894249Sschwartz if (regs_p)
1904249Sschwartz kmem_free(regs_p, regs_length);
1914249Sschwartz bad_regs_p:
1924249Sschwartz if (platform_specific_data)
1934249Sschwartz kmem_free(platform_specific_data, sizeof (fire4u_specific_t));
1944249Sschwartz if (nodename)
1954249Sschwartz kmem_free(nodename, nodename_size);
1964249Sschwartz
1974249Sschwartz return (DDI_FAILURE);
1984249Sschwartz }
1994249Sschwartz
2004249Sschwartz void
fpc_platform_node_fini(void * arg)2014249Sschwartz fpc_platform_node_fini(void *arg)
2024249Sschwartz {
2034249Sschwartz fire4u_specific_t *plat_arg = (fire4u_specific_t *)arg;
2044249Sschwartz if (plat_arg == NULL)
2054249Sschwartz return;
2064249Sschwartz if (plat_arg->nodename)
2074249Sschwartz kmem_free(plat_arg->nodename, strlen(plat_arg->nodename)+1);
2084249Sschwartz kmem_free(plat_arg, sizeof (fire4u_specific_t));
2094249Sschwartz }
2104249Sschwartz
2114249Sschwartz /*ARGSUSED*/
2124249Sschwartz void
fpc_platform_module_fini(dev_info_t * dip)2134249Sschwartz fpc_platform_module_fini(dev_info_t *dip)
2144249Sschwartz {
2154249Sschwartz if (ldi_identifier_valid)
2164249Sschwartz ldi_ident_release(ldi_identifier);
2174249Sschwartz if (credentials)
2184249Sschwartz crfree(credentials);
2194249Sschwartz }
2204249Sschwartz
2214249Sschwartz fire_perfreg_handle_t
fpc_get_perfreg_handle(int devnum)2224249Sschwartz fpc_get_perfreg_handle(int devnum)
2234249Sschwartz {
2244249Sschwartz int rval = EINVAL;
2254249Sschwartz
2264249Sschwartz fire_counter_handle_impl_t *handle_impl =
2274249Sschwartz kmem_zalloc(sizeof (fire_counter_handle_impl_t), KM_SLEEP);
2284249Sschwartz
2294249Sschwartz if ((handle_impl->devspec =
2304249Sschwartz fpc_get_platform_data_by_number(devnum)) != NULL) {
2314249Sschwartz rval = ldi_open_by_name(handle_impl->devspec->nodename,
2324249Sschwartz OPEN_FLAGS, credentials, &handle_impl->devhandle,
2334249Sschwartz ldi_identifier);
2344249Sschwartz }
2354249Sschwartz
2364249Sschwartz if (rval != SUCCESS) {
2374249Sschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
2384249Sschwartz return ((fire_perfreg_handle_t)-1);
2394249Sschwartz } else {
2404249Sschwartz return ((fire_perfreg_handle_t)handle_impl);
2414249Sschwartz }
2424249Sschwartz }
2434249Sschwartz
2444249Sschwartz int
fpc_free_counter_handle(fire_perfreg_handle_t handle)2454249Sschwartz fpc_free_counter_handle(fire_perfreg_handle_t handle)
2464249Sschwartz {
2474249Sschwartz fire_counter_handle_impl_t *handle_impl =
2484249Sschwartz (fire_counter_handle_impl_t *)handle;
2494249Sschwartz (void) ldi_close(handle_impl->devhandle, OPEN_FLAGS, credentials);
2504249Sschwartz kmem_free(handle_impl, sizeof (fire_counter_handle_impl_t));
2514249Sschwartz return (SUCCESS);
2524249Sschwartz }
2534249Sschwartz
2544249Sschwartz int
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)2554249Sschwartz fpc_event_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2564249Sschwartz uint64_t *reg_data, boolean_t is_write)
2574249Sschwartz {
2584249Sschwartz int rval;
2594249Sschwartz int ioctl_rval;
2604249Sschwartz pcitool_reg_t prg;
2614249Sschwartz fire_counter_handle_impl_t *handle_impl =
2624249Sschwartz (fire_counter_handle_impl_t *)handle;
2634249Sschwartz int cmd = is_write ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
2644249Sschwartz
265*4397Sschwartz prg.user_version = PCITOOL_VERSION;
2664249Sschwartz
2674249Sschwartz if (group == jbc) {
2684249Sschwartz prg.barnum = JBUS_BANK;
2694249Sschwartz prg.offset = counter_select_offsets[group] -
2704249Sschwartz handle_impl->devspec->jbus_bank_base;
2714249Sschwartz } else {
2724249Sschwartz prg.barnum = PCIE_BANK;
2734249Sschwartz
2744249Sschwartz /*
2754249Sschwartz * Note that a pcie_bank_base isn't needed. Pcie register
2764249Sschwartz * offsets are already relative to the start of their bank. No
2774249Sschwartz * base needs to be subtracted to get the relative offset that
2784249Sschwartz * pcitool ioctls want.
2794249Sschwartz */
2804249Sschwartz prg.offset = counter_select_offsets[group];
2814249Sschwartz }
2824249Sschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
2834249Sschwartz prg.data = *reg_data;
2844249Sschwartz
2854249Sschwartz /* Read original value. */
2864249Sschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, cmd, (intptr_t)&prg,
2874249Sschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
2884249Sschwartz *reg_data = prg.data;
2894249Sschwartz }
2904249Sschwartz
2914249Sschwartz return (rval);
2924249Sschwartz }
2934249Sschwartz
2944249Sschwartz int
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * value,boolean_t is_write)2954249Sschwartz fpc_counter_io(fire_perfreg_handle_t handle, fire_perfcnt_t group,
2964249Sschwartz int counter_index, uint64_t *value, boolean_t is_write)
2974249Sschwartz {
2984249Sschwartz int rval;
2994249Sschwartz int ioctl_rval;
3004249Sschwartz pcitool_reg_t prg;
3014249Sschwartz fire_counter_handle_impl_t *handle_impl =
3024249Sschwartz (fire_counter_handle_impl_t *)handle;
3034249Sschwartz int command =
3044249Sschwartz (is_write) ? PCITOOL_NEXUS_SET_REG : PCITOOL_NEXUS_GET_REG;
3054249Sschwartz
306*4397Sschwartz prg.user_version = PCITOOL_VERSION;
3074249Sschwartz /*
3084249Sschwartz * Note that stated PCIE offsets are relative to the beginning of their
3094249Sschwartz * register bank, while JBUS offsets are absolute.
3104249Sschwartz */
3114249Sschwartz if (group == jbc) {
3124249Sschwartz prg.barnum = JBUS_BANK;
3134249Sschwartz prg.offset = counter_reg_offsets[counter_index] -
3144249Sschwartz handle_impl->devspec->jbus_bank_base;
3154249Sschwartz } else {
3164249Sschwartz prg.barnum = PCIE_BANK;
3174249Sschwartz prg.offset = counter_reg_offsets[counter_index];
3184249Sschwartz }
3194249Sschwartz
3204249Sschwartz if ((group == lpu) && (is_write)) {
3214249Sschwartz prg.offset += LPU_LINK_PERFCTR_WRITE_OFFSET;
3224249Sschwartz }
3234249Sschwartz
3244249Sschwartz prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 | PCITOOL_ACC_ATTR_ENDN_BIG;
3254249Sschwartz prg.data = *value;
3264249Sschwartz
3274249Sschwartz if (((rval = ldi_ioctl(handle_impl->devhandle, command, (intptr_t)&prg,
3284249Sschwartz FKIOCTL, credentials, &ioctl_rval)) == SUCCESS) && (!is_write)) {
3294249Sschwartz *value = prg.data;
3304249Sschwartz }
3314249Sschwartz
3324249Sschwartz return (rval);
3334249Sschwartz }
334