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
fpc_platform_check()1106528affbSschwartz fpc_platform_check()
1116528affbSschwartz {
1126528affbSschwartz return (SUCCESS);
1136528affbSschwartz }
1146528affbSschwartz
1156528affbSschwartz /* Called during attach to do module-wide initialization. */
1166528affbSschwartz int
fpc_platform_module_init(dev_info_t * dip)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
fpc_platform_node_init(dev_info_t * dip,int * avail)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
fpc_platform_node_fini(void * arg)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
fpc_platform_module_fini(dev_info_t * dip)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
fpc_get_perfreg_handle(int devnum)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
fpc_free_counter_handle(fire_perfreg_handle_t handle)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
fpc_event_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,uint64_t * reg_data,boolean_t is_write)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
fpc_counter_io(fire_perfreg_handle_t handle,fire_perfcnt_t group,int counter_index,uint64_t * value,boolean_t is_write)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