xref: /onnv-gate/usr/src/uts/sun4v/io/n2piupc/n2piupc.c (revision 7656:2621e50fdf4a)
13299Sschwartz /*
23299Sschwartz  * CDDL HEADER START
33299Sschwartz  *
43299Sschwartz  * The contents of this file are subject to the terms of the
53299Sschwartz  * Common Development and Distribution License (the "License").
63299Sschwartz  * You may not use this file except in compliance with the License.
73299Sschwartz  *
83299Sschwartz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93299Sschwartz  * or http://www.opensolaris.org/os/licensing.
103299Sschwartz  * See the License for the specific language governing permissions
113299Sschwartz  * and limitations under the License.
123299Sschwartz  *
133299Sschwartz  * When distributing Covered Code, include this CDDL HEADER in each
143299Sschwartz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153299Sschwartz  * If applicable, add the following below this CDDL HEADER, with the
163299Sschwartz  * fields enclosed by brackets "[]" replaced with your own identifying
173299Sschwartz  * information: Portions Copyright [yyyy] [name of copyright owner]
183299Sschwartz  *
193299Sschwartz  * CDDL HEADER END
203299Sschwartz  */
213299Sschwartz 
223299Sschwartz /*
23*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
243299Sschwartz  * Use is subject to license terms.
253299Sschwartz  */
263299Sschwartz 
273299Sschwartz 
283299Sschwartz /*
293299Sschwartz  * Driver interconnect for the N2 PIU performance counter driver.
303299Sschwartz  */
313299Sschwartz 
323299Sschwartz #include <sys/types.h>
333299Sschwartz #include <sys/ddi.h>
343299Sschwartz #include <sys/modctl.h>
353299Sschwartz #include <sys/hsvc.h>
363299Sschwartz #include <n2piupc_tables.h>
373299Sschwartz #include <n2piupc.h>
383299Sschwartz 
393299Sschwartz /* Debugging level. */
403299Sschwartz #ifdef DEBUG
413299Sschwartz int n2piupc_debug = 0;
423299Sschwartz #endif /* DEBUG */
433299Sschwartz 
443299Sschwartz /* State structure anchor. */
453299Sschwartz void *n2piupc_state_p;
463299Sschwartz 
473299Sschwartz static int n2piupc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
483299Sschwartz static int n2piupc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
493299Sschwartz 
503299Sschwartz /*
513299Sschwartz  * Support for hypervisor versioning.
523299Sschwartz  * Need to negotiate for the N2PIU_PERF_COUNTER_GROUP
533299Sschwartz  */
543299Sschwartz 
553299Sschwartz #define	N2PIUPC_REQ_MAJOR_VER		1
563299Sschwartz #define	N2PIUPC_REQ_MINOR_VER		0
573299Sschwartz 
583299Sschwartz static hsvc_info_t n2piupc_hsvc = {
593299Sschwartz 	HSVC_REV_1,
603299Sschwartz 	NULL,
613299Sschwartz 	N2PIU_PERF_COUNTER_GROUP_ID,
623299Sschwartz 	N2PIUPC_REQ_MAJOR_VER,
633299Sschwartz 	N2PIUPC_REQ_MINOR_VER,
643299Sschwartz 	MODULE_NAME	/* Passed in as a #define from Makefile */
653299Sschwartz };
663299Sschwartz 
673299Sschwartz static uint64_t	n2piupc_sup_minor;
683299Sschwartz 
693299Sschwartz /* Driver boilerplate stuff.  Having no minor nodes keep things very simple. */
703299Sschwartz 
713299Sschwartz static struct dev_ops n2piupc_ops = {
723299Sschwartz 	DEVO_REV,
733299Sschwartz 	0,
743299Sschwartz 	nulldev,
753299Sschwartz 	nulldev,
763299Sschwartz 	nulldev,
773299Sschwartz 	n2piupc_attach,
783299Sschwartz 	n2piupc_detach,
793299Sschwartz 	nodev,
803299Sschwartz 	NULL,
813299Sschwartz 	NULL,
82*7656SSherry.Moore@Sun.COM 	nodev,
83*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,
843299Sschwartz };
853299Sschwartz 
863299Sschwartz extern struct mod_ops mod_driverops;
873299Sschwartz 
883299Sschwartz static struct modldrv md = {
893299Sschwartz 	&mod_driverops,
90*7656SSherry.Moore@Sun.COM 	"N2 PIU Perf Counter",
913299Sschwartz 	&n2piupc_ops,
923299Sschwartz };
933299Sschwartz 
943299Sschwartz static struct modlinkage ml = {
953299Sschwartz 	MODREV_1,
963299Sschwartz 	(void *)&md,
973299Sschwartz 	NULL
983299Sschwartz };
993299Sschwartz 
1003299Sschwartz 
1013299Sschwartz /*
1023299Sschwartz  * One-time module-wide initialization.
1033299Sschwartz  */
1043299Sschwartz int
_init(void)1053299Sschwartz _init(void)
1063299Sschwartz {
1073299Sschwartz 	int rval;
1083299Sschwartz 
1093299Sschwartz 	/* Negotiate for hypervisor support. */
1103299Sschwartz 	if ((rval = hsvc_register(&n2piupc_hsvc, &n2piupc_sup_minor)) !=
1113299Sschwartz 	    DDI_SUCCESS) {
1123299Sschwartz 		N2PIUPC_DBG1("%s: Could not hsvc_register: %d\n",
1133299Sschwartz 		    MODULE_NAME, rval);
1143299Sschwartz 		goto bad_hv_register;
1153299Sschwartz 	}
1163299Sschwartz 
1173299Sschwartz 	/* Initialize per-leaf soft state pointer. */
1183299Sschwartz 	if ((rval = ddi_soft_state_init(&n2piupc_state_p,
1193299Sschwartz 	    sizeof (n2piupc_t), 1)) != DDI_SUCCESS)
1203299Sschwartz 		goto bad_softstate_init;
1213299Sschwartz 
1223299Sschwartz 	/* Initialize one-time kstat structures. */
1233299Sschwartz 	if ((rval = n2piupc_kstat_init()) != DDI_SUCCESS)
1243299Sschwartz 		goto bad_kstat_init;
1253299Sschwartz 
1263299Sschwartz 	/* If all checks out, install the module. */
1273299Sschwartz 	if ((rval = mod_install(&ml)) == DDI_SUCCESS)
1283299Sschwartz 
1293299Sschwartz 		return (DDI_SUCCESS);
1303299Sschwartz 
1313299Sschwartz bad_mod_install:
1323299Sschwartz 	n2piupc_kstat_fini();
1333299Sschwartz bad_kstat_init:
1343299Sschwartz 	ddi_soft_state_fini(&n2piupc_state_p);
1353299Sschwartz bad_softstate_init:
1363299Sschwartz 	(void) hsvc_unregister(&n2piupc_hsvc);
1373299Sschwartz bad_hv_register:
1383299Sschwartz 	return (rval);
1393299Sschwartz }
1403299Sschwartz 
1413299Sschwartz /*
1423299Sschwartz  * One-time module-wide cleanup, after last detach is done.
1433299Sschwartz  */
1443299Sschwartz int
_fini(void)1453299Sschwartz _fini(void)
1463299Sschwartz {
1473299Sschwartz 	int rval;
1483299Sschwartz 
1493299Sschwartz 	/*
1503299Sschwartz 	 * Remove the module first as this operation is the only thing here
1513299Sschwartz 	 * which can fail.
1523299Sschwartz 	 */
1533299Sschwartz 	rval = mod_remove(&ml);
1543299Sschwartz 	if (rval != DDI_SUCCESS)
1553299Sschwartz 		return (rval);
1563299Sschwartz 
1573299Sschwartz 	/* One-shot kstat data structure cleanup. */
1583299Sschwartz 	n2piupc_kstat_fini();
1593299Sschwartz 
1603299Sschwartz 	/* Free px soft state */
1613299Sschwartz 	ddi_soft_state_fini(&n2piupc_state_p);
1623299Sschwartz 
1633299Sschwartz 	/* Unregister with hypervisor. */
1643299Sschwartz 	(void) hsvc_unregister(&n2piupc_hsvc);
1653299Sschwartz 
1663299Sschwartz 	return (rval);
1673299Sschwartz }
1683299Sschwartz 
1693299Sschwartz int
_info(struct modinfo * modinfop)1703299Sschwartz _info(struct modinfo *modinfop)
1713299Sschwartz {
1723299Sschwartz 	return (mod_info(&ml, modinfop));
1733299Sschwartz }
1743299Sschwartz 
1753299Sschwartz /*
1763299Sschwartz  * Per-instance initialization.  Suspend/resume not supported.
1773299Sschwartz  */
1783299Sschwartz static int
n2piupc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1793299Sschwartz n2piupc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1803299Sschwartz {
1813299Sschwartz 	n2piupc_t *n2piupc_p;
1823299Sschwartz 	uint32_t regprop[4];
1833299Sschwartz 	int len;
1843299Sschwartz 	int instance = ddi_get_instance(dip);
1853299Sschwartz 
1863299Sschwartz 	switch (cmd) {
1873299Sschwartz 	case DDI_RESUME:
1883299Sschwartz 	case DDI_ATTACH:
1893299Sschwartz 		if (ddi_soft_state_zalloc(n2piupc_state_p, instance) !=
1903299Sschwartz 		    DDI_SUCCESS) {
1913299Sschwartz 			cmn_err(CE_WARN, "%s%d: Can't allocate softstate.\n",
1923299Sschwartz 			    NAMEINST(dip));
1933299Sschwartz 			goto bad_softstate;
1943299Sschwartz 		}
1953299Sschwartz 
1963299Sschwartz 		n2piupc_p = (n2piupc_t *)ddi_get_soft_state(n2piupc_state_p,
1973299Sschwartz 		    instance);
1983299Sschwartz 
1993299Sschwartz 		n2piupc_p->n2piupc_dip = dip;
2003299Sschwartz 
2013299Sschwartz 		/* Get handle for hypervisor access of performance counters. */
2023299Sschwartz 		len = sizeof (regprop);
2033299Sschwartz 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2043299Sschwartz 		    "reg", (caddr_t)regprop, &len) != DDI_SUCCESS) {
2053299Sschwartz 
2063299Sschwartz 			cmn_err(CE_WARN,
2073299Sschwartz 			    "%s%d: Cannot get reg property\n",
2083299Sschwartz 			    NAMEINST(dip));
2093299Sschwartz 			goto bad_handle;
2103299Sschwartz 		}
2113299Sschwartz 
2123299Sschwartz 		/* Look only at the lower 28 bits of the highest cell. */
2133299Sschwartz 		n2piupc_p->n2piupc_handle = regprop[0] & 0xfffffff;
2143299Sschwartz 
2153299Sschwartz 		/* Set up kstats. */
2163299Sschwartz 		if (n2piupc_kstat_attach(n2piupc_p) != DDI_SUCCESS)
2173299Sschwartz 			goto bad_kstat_attach;
2183299Sschwartz 
2193299Sschwartz 		return (DDI_SUCCESS);
2203299Sschwartz 
2213299Sschwartz bad_kstat_attach:
2223299Sschwartz bad_handle:
2233299Sschwartz 		(void) ddi_soft_state_free(n2piupc_state_p, instance);
2243299Sschwartz bad_softstate:
2253299Sschwartz 		return (DDI_FAILURE);
2263299Sschwartz 
2273299Sschwartz 	default:
2283299Sschwartz 		return (DDI_FAILURE);
2293299Sschwartz 	}
2303299Sschwartz }
2313299Sschwartz 
2323299Sschwartz /*
2333299Sschwartz  * Per-instance cleanup.  Suspend/resume not supported.
2343299Sschwartz  */
2353299Sschwartz static int
n2piupc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)2363299Sschwartz n2piupc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2373299Sschwartz {
2383299Sschwartz 	int instance = ddi_get_instance(dip);
2393299Sschwartz 
2403299Sschwartz 	n2piupc_t *n2piupc_p = (n2piupc_t *)ddi_get_soft_state(
2413299Sschwartz 	    n2piupc_state_p, instance);
2423299Sschwartz 
2433299Sschwartz 	switch (cmd) {
2443299Sschwartz 	case DDI_SUSPEND:
2453299Sschwartz 	case DDI_DETACH:
2463299Sschwartz 		n2piupc_kstat_detach(n2piupc_p);
2473299Sschwartz 		(void) ddi_soft_state_free(n2piupc_state_p, instance);
2483299Sschwartz 
2493299Sschwartz 		return (DDI_SUCCESS);
2503299Sschwartz 
2513299Sschwartz 	default:
2523299Sschwartz 		return (DDI_FAILURE);
2533299Sschwartz 	}
2543299Sschwartz }
255