xref: /illumos-gate/usr/src/uts/sun4v/io/n2piupc/n2piupc.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 /*
29  * Driver interconnect for the N2 PIU performance counter driver.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/ddi.h>
34 #include <sys/modctl.h>
35 #include <sys/hsvc.h>
36 #include <n2piupc_tables.h>
37 #include <n2piupc.h>
38 
39 /* Debugging level. */
40 #ifdef DEBUG
41 int n2piupc_debug = 0;
42 #endif /* DEBUG */
43 
44 /* State structure anchor. */
45 void *n2piupc_state_p;
46 
47 static int n2piupc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
48 static int n2piupc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
49 
50 /*
51  * Support for hypervisor versioning.
52  * Need to negotiate for the N2PIU_PERF_COUNTER_GROUP
53  */
54 
55 #define	N2PIUPC_REQ_MAJOR_VER		1
56 #define	N2PIUPC_REQ_MINOR_VER		0
57 
58 static hsvc_info_t n2piupc_hsvc = {
59 	HSVC_REV_1,
60 	NULL,
61 	N2PIU_PERF_COUNTER_GROUP_ID,
62 	N2PIUPC_REQ_MAJOR_VER,
63 	N2PIUPC_REQ_MINOR_VER,
64 	MODULE_NAME	/* Passed in as a #define from Makefile */
65 };
66 
67 static uint64_t	n2piupc_sup_minor;
68 
69 /* Driver boilerplate stuff.  Having no minor nodes keep things very simple. */
70 
71 static struct dev_ops n2piupc_ops = {
72 	DEVO_REV,
73 	0,
74 	nulldev,
75 	nulldev,
76 	nulldev,
77 	n2piupc_attach,
78 	n2piupc_detach,
79 	nodev,
80 	NULL,
81 	NULL,
82 	nodev,
83 	ddi_quiesce_not_needed,
84 };
85 
86 extern struct mod_ops mod_driverops;
87 
88 static struct modldrv md = {
89 	&mod_driverops,
90 	"N2 PIU Perf Counter",
91 	&n2piupc_ops,
92 };
93 
94 static struct modlinkage ml = {
95 	MODREV_1,
96 	(void *)&md,
97 	NULL
98 };
99 
100 
101 /*
102  * One-time module-wide initialization.
103  */
104 int
105 _init(void)
106 {
107 	int rval;
108 
109 	/* Negotiate for hypervisor support. */
110 	if ((rval = hsvc_register(&n2piupc_hsvc, &n2piupc_sup_minor)) !=
111 	    DDI_SUCCESS) {
112 		N2PIUPC_DBG1("%s: Could not hsvc_register: %d\n",
113 		    MODULE_NAME, rval);
114 		goto bad_hv_register;
115 	}
116 
117 	/* Initialize per-leaf soft state pointer. */
118 	if ((rval = ddi_soft_state_init(&n2piupc_state_p,
119 	    sizeof (n2piupc_t), 1)) != DDI_SUCCESS)
120 		goto bad_softstate_init;
121 
122 	/* Initialize one-time kstat structures. */
123 	if ((rval = n2piupc_kstat_init()) != DDI_SUCCESS)
124 		goto bad_kstat_init;
125 
126 	/* If all checks out, install the module. */
127 	if ((rval = mod_install(&ml)) == DDI_SUCCESS)
128 
129 		return (DDI_SUCCESS);
130 
131 bad_mod_install:
132 	n2piupc_kstat_fini();
133 bad_kstat_init:
134 	ddi_soft_state_fini(&n2piupc_state_p);
135 bad_softstate_init:
136 	(void) hsvc_unregister(&n2piupc_hsvc);
137 bad_hv_register:
138 	return (rval);
139 }
140 
141 /*
142  * One-time module-wide cleanup, after last detach is done.
143  */
144 int
145 _fini(void)
146 {
147 	int rval;
148 
149 	/*
150 	 * Remove the module first as this operation is the only thing here
151 	 * which can fail.
152 	 */
153 	rval = mod_remove(&ml);
154 	if (rval != DDI_SUCCESS)
155 		return (rval);
156 
157 	/* One-shot kstat data structure cleanup. */
158 	n2piupc_kstat_fini();
159 
160 	/* Free px soft state */
161 	ddi_soft_state_fini(&n2piupc_state_p);
162 
163 	/* Unregister with hypervisor. */
164 	(void) hsvc_unregister(&n2piupc_hsvc);
165 
166 	return (rval);
167 }
168 
169 int
170 _info(struct modinfo *modinfop)
171 {
172 	return (mod_info(&ml, modinfop));
173 }
174 
175 /*
176  * Per-instance initialization.  Suspend/resume not supported.
177  */
178 static int
179 n2piupc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
180 {
181 	n2piupc_t *n2piupc_p;
182 	uint32_t regprop[4];
183 	int len;
184 	int instance = ddi_get_instance(dip);
185 
186 	switch (cmd) {
187 	case DDI_RESUME:
188 	case DDI_ATTACH:
189 		if (ddi_soft_state_zalloc(n2piupc_state_p, instance) !=
190 		    DDI_SUCCESS) {
191 			cmn_err(CE_WARN, "%s%d: Can't allocate softstate.\n",
192 			    NAMEINST(dip));
193 			goto bad_softstate;
194 		}
195 
196 		n2piupc_p = (n2piupc_t *)ddi_get_soft_state(n2piupc_state_p,
197 		    instance);
198 
199 		n2piupc_p->n2piupc_dip = dip;
200 
201 		/* Get handle for hypervisor access of performance counters. */
202 		len = sizeof (regprop);
203 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
204 		    "reg", (caddr_t)regprop, &len) != DDI_SUCCESS) {
205 
206 			cmn_err(CE_WARN,
207 			    "%s%d: Cannot get reg property\n",
208 			    NAMEINST(dip));
209 			goto bad_handle;
210 		}
211 
212 		/* Look only at the lower 28 bits of the highest cell. */
213 		n2piupc_p->n2piupc_handle = regprop[0] & 0xfffffff;
214 
215 		/* Set up kstats. */
216 		if (n2piupc_kstat_attach(n2piupc_p) != DDI_SUCCESS)
217 			goto bad_kstat_attach;
218 
219 		return (DDI_SUCCESS);
220 
221 bad_kstat_attach:
222 bad_handle:
223 		(void) ddi_soft_state_free(n2piupc_state_p, instance);
224 bad_softstate:
225 		return (DDI_FAILURE);
226 
227 	default:
228 		return (DDI_FAILURE);
229 	}
230 }
231 
232 /*
233  * Per-instance cleanup.  Suspend/resume not supported.
234  */
235 static int
236 n2piupc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
237 {
238 	int instance = ddi_get_instance(dip);
239 
240 	n2piupc_t *n2piupc_p = (n2piupc_t *)ddi_get_soft_state(
241 	    n2piupc_state_p, instance);
242 
243 	switch (cmd) {
244 	case DDI_SUSPEND:
245 	case DDI_DETACH:
246 		n2piupc_kstat_detach(n2piupc_p);
247 		(void) ddi_soft_state_free(n2piupc_state_p, instance);
248 
249 		return (DDI_SUCCESS);
250 
251 	default:
252 		return (DDI_FAILURE);
253 	}
254 }
255