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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
n2piupc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
n2piupc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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