xref: /titanic_52/usr/src/uts/sun4u/chicago/io/fpc/fpc-impl-4u.c (revision 2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9)
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)&regs_p, &regs_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