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