xref: /illumos-gate/usr/src/uts/sun4u/pcbe/opl_pcbe.c (revision 4abb96737d15cd2d6530b0aa7b8404ec911ad940)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * SPARC64 VI Performance Counter Backend
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/cpuvar.h>
33 #include <sys/systm.h>
34 #include <sys/cmn_err.h>
35 #include <sys/cpc_impl.h>
36 #include <sys/cpc_pcbe.h>
37 #include <sys/modctl.h>
38 #include <sys/machsystm.h>
39 #include <sys/sdt.h>
40 #include <sys/cpu_impl.h>
41 
42 static int opl_pcbe_init(void);
43 static uint_t opl_pcbe_ncounters(void);
44 static const char *opl_pcbe_impl_name(void);
45 static const char *opl_pcbe_cpuref(void);
46 static char *opl_pcbe_list_events(uint_t picnum);
47 static char *opl_pcbe_list_attrs(void);
48 static uint64_t opl_pcbe_event_coverage(char *event);
49 static uint64_t opl_pcbe_overflow_bitmap(void);
50 static int opl_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
51     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
52     void *token);
53 static void opl_pcbe_program(void *token);
54 static void opl_pcbe_allstop(void);
55 static void opl_pcbe_sample(void *token);
56 static void opl_pcbe_free(void *config);
57 
58 extern void ultra_setpcr(uint64_t);
59 extern uint64_t ultra_getpcr(void);
60 extern void ultra_setpic(uint64_t);
61 extern uint64_t ultra_getpic(void);
62 extern uint64_t ultra_gettick(void);
63 
64 pcbe_ops_t opl_pcbe_ops = {
65 	PCBE_VER_1,
66 	CPC_CAP_OVERFLOW_INTERRUPT,
67 	opl_pcbe_ncounters,
68 	opl_pcbe_impl_name,
69 	opl_pcbe_cpuref,
70 	opl_pcbe_list_events,
71 	opl_pcbe_list_attrs,
72 	opl_pcbe_event_coverage,
73 	opl_pcbe_overflow_bitmap,
74 	opl_pcbe_configure,
75 	opl_pcbe_program,
76 	opl_pcbe_allstop,
77 	opl_pcbe_sample,
78 	opl_pcbe_free
79 };
80 
81 typedef struct _opl_pcbe_config {
82 	uint8_t		opl_picno;	/* From 0 to 7 */
83 	uint32_t	opl_bits;	/* %pcr event code unshifted */
84 	uint32_t	opl_flags;	/* user/system/priv */
85 	uint32_t	opl_pic;	/* unshifted raw %pic value */
86 } opl_pcbe_config_t;
87 
88 struct nametable {
89 	const uint8_t	bits;
90 	const char	*name;
91 };
92 
93 #define	PIC_MASK (((uint64_t)1 << 32) - 1)
94 
95 #define	SPARC64_VI_PCR_PRIVPIC	UINT64_C(1)
96 
97 #define	CPC_SPARC64_VI_PCR_USR_SHIFT	2
98 #define	CPC_SPARC64_VI_PCR_SYS_SHIFT	1
99 
100 #define	CPC_SPARC64_VI_PCR_PICL_SHIFT	4
101 #define	CPC_SPARC64_VI_PCR_PICU_SHIFT	11
102 #define	CPC_SPARC64_VI_PCR_PIC_MASK	UINT64_C(0x3f)
103 
104 #define	CPC_SPARC64_VI_NPIC		8
105 
106 #define	CPC_SPARC64_VI_PCR_ULRO_SHIFT	3
107 #define	CPC_SPARC64_VI_PCR_SC_SHIFT	18
108 #define	CPC_SPARC64_VI_PCR_SC_MASK	UINT64_C(0x7)
109 #define	CPC_SPARC64_VI_PCR_NC_SHIFT	22
110 #define	CPC_SPARC64_VI_PCR_NC_MASK	UINT64_C(0x7)
111 #define	CPC_SPARC64_VI_PCR_OVRO_SHIFT	26
112 #define	CPC_SPARC64_VI_PCR_OVF_SHIFT	32
113 #define	CPC_SPARC64_VI_PCR_OVF_MASK	UINT64_C(0xffff)
114 
115 #define	SPARC64_VI_PCR_SYS	(UINT64_C(1) << CPC_SPARC64_VI_PCR_SYS_SHIFT)
116 #define	SPARC64_VI_PCR_USR	(UINT64_C(1) << CPC_SPARC64_VI_PCR_USR_SHIFT)
117 #define	SPARC64_VI_PCR_ULRO	(UINT64_C(1) << CPC_SPARC64_VI_PCR_ULRO_SHIFT)
118 #define	SPARC64_VI_PCR_OVRO	(UINT64_C(1) << CPC_SPARC64_VI_PCR_OVRO_SHIFT)
119 #define	SPARC64_VI_PCR_OVF	(CPC_SPARC64_VI_PCR_OVF_MASK << \
120 					CPC_SPARC64_VI_PCR_OVF_SHIFT)
121 
122 #define	SPARC64_VI_NUM_PIC_PAIRS	4
123 
124 #define	SPARC64_VI_PCR_SEL_PIC(pcr, picno) {				\
125 	pcr &= ~((CPC_SPARC64_VI_PCR_SC_MASK				\
126 		<< CPC_SPARC64_VI_PCR_SC_SHIFT));			\
127 									\
128 	pcr |= (((picno) & CPC_SPARC64_VI_PCR_SC_MASK)			\
129 		<< CPC_SPARC64_VI_PCR_SC_SHIFT);			\
130 }
131 
132 #define	SPARC64_VI_PCR_SEL_EVENT(pcr, sl, su) {				\
133 	pcr &= ~((CPC_SPARC64_VI_PCR_PIC_MASK				\
134 		<< CPC_SPARC64_VI_PCR_PICL_SHIFT)			\
135 	    | (CPC_SPARC64_VI_PCR_PIC_MASK				\
136 		<< CPC_SPARC64_VI_PCR_PICU_SHIFT));			\
137 									\
138 	pcr |= (((sl) & CPC_SPARC64_VI_PCR_PIC_MASK)			\
139 		<< CPC_SPARC64_VI_PCR_PICL_SHIFT);			\
140 	pcr |= (((su) & CPC_SPARC64_VI_PCR_PIC_MASK)			\
141 		<< CPC_SPARC64_VI_PCR_PICU_SHIFT);			\
142 }
143 
144 #define	NT_END 0xFF
145 
146 static const uint64_t   allstopped = SPARC64_VI_PCR_PRIVPIC |
147 	SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO;
148 
149 #define	SPARC64_VI_EVENTS_comm			\
150 	{0x0,	"cycle_counts"},		\
151 	{0x1,	"instruction_counts"},		\
152 	{0x8,	"load_store_instructions"},	\
153 	{0x9,	"branch_instructions"},		\
154 	{0xa,	"floating_instructions"},	\
155 	{0xb,	"impdep2_instructions"},	\
156 	{0xc,	"prefetch_instructions"}
157 
158 static const struct nametable SPARC64_VI_names_l0[] = {
159 	SPARC64_VI_EVENTS_comm,
160 	{0x16,	"trap_int_vector"},
161 	{0x20,	"write_op_uTLB"},
162 	{0x30,	"sx_miss_wait_pf"},
163 	{0x31,	"jbus_cpi_count"},
164 	{NT_END, ""}
165 };
166 
167 static const struct nametable SPARC64_VI_names_u0[] = {
168 	SPARC64_VI_EVENTS_comm,
169 	{0x16,	"trap_all"},
170 	{0x20,	"write_if_uTLB"},
171 	{0x30,	"sx_miss_wait_dm"},
172 	{0x31,	"jbus_bi_count"},
173 	{NT_END, ""}
174 };
175 
176 static const struct nametable SPARC64_VI_names_l1[] = {
177 	SPARC64_VI_EVENTS_comm,
178 	{0x16,	"trap_spill"},
179 	{0x20,	"write_op_uTLB"},
180 	{0x30,	"sx_miss_count_pf"},
181 	{0x31,	"jbus_cpd_count"},
182 	{NT_END, ""}
183 };
184 
185 static const struct nametable SPARC64_VI_names_u1[] = {
186 	SPARC64_VI_EVENTS_comm,
187 	{0x16,	"trap_int_level"},
188 	{0x20,	"write_if_uTLB"},
189 	{0x30,	"sx_miss_count_dm"},
190 	{0x31,	"jbus_cpb_count"},
191 	{NT_END, ""}
192 };
193 
194 static const struct nametable SPARC64_VI_names_l2[] = {
195 	SPARC64_VI_EVENTS_comm,
196 	{0x16,	"trap_trap_inst"},
197 	{0x20,	"op_r_iu_req_mi_go"},
198 	{0x30,	"sx_read_count_pf"},
199 	{NT_END, ""}
200 };
201 
202 static const struct nametable SPARC64_VI_names_u2[] = {
203 	SPARC64_VI_EVENTS_comm,
204 	{0x16,	"trap_fill"},
205 	{0x20,	"if_r_iu_req_mi_go"},
206 	{0x30,	"sx_read_count_dm"},
207 	{NT_END, ""}
208 };
209 
210 static const struct nametable SPARC64_VI_names_l3[] = {
211 	SPARC64_VI_EVENTS_comm,
212 	{0x16,	"trap_DMMU_miss"},
213 	{0x20,	"op_wait_all"},
214 	{0x30,	"dvp_count_pf"},
215 	{NT_END, ""}
216 };
217 
218 static const struct nametable SPARC64_VI_names_u3[] = {
219 	SPARC64_VI_EVENTS_comm,
220 	{0x16,	"trap_IMMU_miss"},
221 	{0x20,	"if_wait_all"},
222 	{0x30,	"dvp_count_dm"},
223 	{NT_END, ""}
224 };
225 
226 #undef	SPARC64_VI_EVENTS_comm
227 
228 static const struct nametable *SPARC64_VI_names[CPC_SPARC64_VI_NPIC] = {
229 	SPARC64_VI_names_l0,
230 	SPARC64_VI_names_u0,
231 	SPARC64_VI_names_l1,
232 	SPARC64_VI_names_u1,
233 	SPARC64_VI_names_l2,
234 	SPARC64_VI_names_u2,
235 	SPARC64_VI_names_l3,
236 	SPARC64_VI_names_u3
237 };
238 
239 opl_pcbe_config_t nullpic[CPC_SPARC64_VI_NPIC] = {
240 	{0, 0x3f, 0, 0},
241 	{1, 0x3f, 0, 0},
242 	{2, 0x3f, 0, 0},
243 	{3, 0x3f, 0, 0},
244 	{4, 0x3f, 0, 0},
245 	{5, 0x3f, 0, 0},
246 	{6, 0x3f, 0, 0},
247 	{7, 0x3f, 0, 0}
248 };
249 
250 static const struct nametable **events;
251 static const char *opl_impl_name;
252 static const char *opl_cpuref;
253 static char *pic_events[CPC_SPARC64_VI_NPIC];
254 
255 static const char *sp_6_ref = "See the \"SPARC64 VI User's Manual\"  "
256 			"for descriptions of these events.";
257 
258 static int
259 opl_pcbe_init(void)
260 {
261 	const struct nametable	*n;
262 	int			i;
263 	size_t			size;
264 
265 	/*
266 	 * Discover type of CPU
267 	 *
268 	 * Point nametable to that CPU's table
269 	 */
270 	switch (ULTRA_VER_IMPL(ultra_getver())) {
271 	case OLYMPUS_C_IMPL:
272 		events = SPARC64_VI_names;
273 		opl_impl_name = "SPARC64 VI";
274 		opl_cpuref = sp_6_ref;
275 		break;
276 	default:
277 		return (-1);
278 	}
279 
280 	/*
281 	 * Initialize the list of events for each PIC.
282 	 * Do two passes: one to compute the size necessary and another
283 	 * to copy the strings. Need room for event, comma, and NULL terminator.
284 	 */
285 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
286 		size = 0;
287 		for (n = events[i]; n->bits != NT_END; n++)
288 			size += strlen(n->name) + 1;
289 		pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
290 		*pic_events[i] = '\0';
291 		for (n = events[i]; n->bits != NT_END; n++) {
292 			(void) strcat(pic_events[i], n->name);
293 			(void) strcat(pic_events[i], ",");
294 		}
295 		/*
296 		 * Remove trailing comma.
297 		 */
298 		pic_events[i][size - 1] = '\0';
299 	}
300 
301 	return (0);
302 }
303 
304 static uint_t
305 opl_pcbe_ncounters(void)
306 {
307 	return (CPC_SPARC64_VI_NPIC);
308 }
309 
310 static const char *
311 opl_pcbe_impl_name(void)
312 {
313 	return (opl_impl_name);
314 }
315 
316 static const char *
317 opl_pcbe_cpuref(void)
318 {
319 	return (opl_cpuref);
320 }
321 
322 static char *
323 opl_pcbe_list_events(uint_t picnum)
324 {
325 	ASSERT(picnum >= 0 && picnum < cpc_ncounters);
326 
327 	return (pic_events[picnum]);
328 }
329 
330 static char *
331 opl_pcbe_list_attrs(void)
332 {
333 	return ("");
334 }
335 
336 static const struct nametable *
337 find_event(int regno, char *name)
338 {
339 	const struct nametable *n;
340 
341 	n = events[regno];
342 
343 	for (; n->bits != NT_END; n++)
344 		if (strcmp(name, n->name) == 0)
345 			return (n);
346 
347 	return (NULL);
348 }
349 
350 static uint64_t
351 opl_pcbe_event_coverage(char *event)
352 {
353 	uint64_t bitmap = 0;
354 
355 	int	i;
356 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
357 		if (find_event(i, event) != NULL)
358 			bitmap |= (1 << i);
359 	}
360 
361 	return (bitmap);
362 }
363 
364 /*
365  * XXX: Need to check if overflow bits can be cleared here.
366  */
367 static uint64_t
368 opl_pcbe_overflow_bitmap(void)
369 {
370 	uint64_t	pcr;
371 
372 	pcr = ultra_getpcr();
373 	return ((pcr & SPARC64_VI_PCR_OVF) >> CPC_SPARC64_VI_PCR_OVF_SHIFT);
374 }
375 
376 /*ARGSUSED*/
377 static int
378 opl_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
379     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
380 {
381 	opl_pcbe_config_t *conf;
382 	const struct nametable *n;
383 	opl_pcbe_config_t *other_config;
384 
385 	/*
386 	 * If we've been handed an existing configuration, we need only preset
387 	 * the counter value.
388 	 */
389 	if (*data != NULL) {
390 		conf = *data;
391 		conf->opl_pic = (uint32_t)preset;
392 		return (0);
393 	}
394 
395 	if (picnum < 0 || picnum >= CPC_SPARC64_VI_NPIC)
396 		return (CPC_INVALID_PICNUM);
397 
398 	if (nattrs != 0)
399 		return (CPC_INVALID_ATTRIBUTE);
400 
401 	/*
402 	 * Find other requests that will be programmed with this one, and ensure
403 	 * the flags don't conflict.
404 	 */
405 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
406 	    (other_config->opl_flags != flags))
407 		return (CPC_CONFLICTING_REQS);
408 
409 	if ((n = find_event(picnum, event)) == NULL)
410 		return (CPC_INVALID_EVENT);
411 
412 	conf = kmem_alloc(sizeof (opl_pcbe_config_t), KM_SLEEP);
413 
414 	conf->opl_picno = picnum;
415 	conf->opl_bits = (uint32_t)n->bits;
416 	conf->opl_flags = flags;
417 	conf->opl_pic = (uint32_t)preset;
418 
419 	*data = conf;
420 	return (0);
421 }
422 
423 static void
424 opl_pcbe_program(void *token)
425 {
426 	opl_pcbe_config_t	*pic[CPC_SPARC64_VI_NPIC];
427 	opl_pcbe_config_t	*firstconfig;
428 	opl_pcbe_config_t	*tmp;
429 	uint64_t		pcr;
430 	uint64_t		curpic;
431 	uint8_t			bitmap = 0;	/* for used pic config */
432 	int			i;
433 	opl_pcbe_config_t	dummypic[CPC_SPARC64_VI_NPIC];
434 
435 	/* Get next pic config */
436 	firstconfig = tmp = kcpc_next_config(token, NULL, NULL);
437 
438 	while (tmp != NULL) {
439 		ASSERT(tmp->opl_picno < CPC_SPARC64_VI_NPIC);
440 		ASSERT(firstconfig->opl_flags == tmp->opl_flags);
441 		pic[tmp->opl_picno] = tmp;
442 		bitmap |= (uint8_t)(1 << tmp->opl_picno);
443 		tmp = kcpc_next_config(token, tmp, NULL);
444 	}
445 	if (bitmap == 0)
446 		panic("opl_pcbe: token %p has no configs", token);
447 
448 	/* Fill in unused pic config */
449 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
450 		if (bitmap & (1 << i))
451 			continue;
452 
453 		dummypic[i] = nullpic[i];
454 		dummypic[i].opl_flags = firstconfig->opl_flags;
455 		pic[i] = &nullpic[i];
456 	}
457 
458 	/*
459 	 * For each counter pair, initialize event settings and
460 	 * counter values.
461 	 */
462 	ultra_setpcr(allstopped);
463 	pcr = allstopped;
464 	pcr &= ~SPARC64_VI_PCR_ULRO;
465 	for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) {
466 		SPARC64_VI_PCR_SEL_PIC(pcr, i);
467 		SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits,
468 		    pic[i*2 + 1]->opl_bits);
469 
470 		ultra_setpcr(pcr);
471 		curpic = (uint64_t)(pic[i*2]->opl_pic |
472 		    ((uint64_t)pic[i*2 + 1]->opl_pic << 32));
473 		ultra_setpic(curpic);
474 	}
475 
476 	/*
477 	 * For each counter pair, enable the trace flags to start
478 	 * counting. Re-read the counters to sample the counter value now
479 	 * and use that as the baseline for future samples.
480 	 */
481 
482 	/* Set pcr */
483 	pcr = ultra_getpcr();
484 	pcr |= (SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO);
485 	if (pic[0]->opl_flags & CPC_COUNT_USER)
486 		pcr |= SPARC64_VI_PCR_USR;
487 	if (pic[0]->opl_flags & CPC_COUNT_SYSTEM)
488 		pcr |= SPARC64_VI_PCR_SYS;
489 
490 	/* Set counter values */
491 	for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) {
492 		SPARC64_VI_PCR_SEL_PIC(pcr, i);
493 		SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits,
494 		    pic[i*2 + 1]->opl_bits);
495 
496 		ultra_setpcr(pcr);
497 		curpic = ultra_getpic();
498 		pic[i*2]->opl_pic = (uint32_t)(curpic & PIC_MASK);
499 		pic[i*2 + 1]->opl_pic = (uint32_t)(curpic >> 32);
500 	}
501 
502 }
503 
504 static void
505 opl_pcbe_allstop(void)
506 {
507 	ultra_setpcr(allstopped);
508 }
509 
510 
511 static void
512 opl_pcbe_sample(void *token)
513 {
514 	uint64_t		curpic;
515 	uint64_t		pcr;
516 	int64_t			diff;
517 	uint64_t		*pic_data[CPC_SPARC64_VI_NPIC];
518 	uint64_t		*dtmp;
519 	opl_pcbe_config_t	*pic[CPC_SPARC64_VI_NPIC];
520 	opl_pcbe_config_t	*ctmp;
521 	opl_pcbe_config_t	*firstconfig;
522 	uint8_t			bitmap = 0;	/* for used pic config */
523 	int			i;
524 	opl_pcbe_config_t dummypic[CPC_SPARC64_VI_NPIC];
525 	uint64_t dummypic_data[CPC_SPARC64_VI_NPIC];
526 
527 	/* Get next pic config */
528 	firstconfig = ctmp = kcpc_next_config(token, NULL, &dtmp);
529 
530 	while (ctmp != NULL) {
531 		ASSERT(ctmp->opl_picno < CPC_SPARC64_VI_NPIC);
532 		ASSERT(firstconfig->opl_flags == ctmp->opl_flags);
533 		pic[ctmp->opl_picno] = ctmp;
534 		pic_data[ctmp->opl_picno] = dtmp;
535 		bitmap |= (uint8_t)(1 << ctmp->opl_picno);
536 		ctmp = kcpc_next_config(token, ctmp, &dtmp);
537 	}
538 	if (bitmap == 0)
539 		panic("opl_pcbe: token %p has no configs", token);
540 
541 	/* Fill in unuse pic config */
542 	for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) {
543 		if (bitmap & (1 << i))
544 			continue;
545 
546 		dummypic[i] = nullpic[i];
547 		dummypic[i].opl_flags = firstconfig->opl_flags;
548 		pic[i] = &dummypic[i];
549 
550 		dummypic_data[i] = 0;
551 		pic_data[i] = &dummypic_data[i];
552 	}
553 
554 	pcr = ultra_getpcr();
555 	pcr |= (SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO);
556 
557 	for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) {
558 		SPARC64_VI_PCR_SEL_PIC(pcr, i);
559 		SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits,
560 		    pic[i*2 + 1]->opl_bits);
561 
562 		ultra_setpcr(pcr);
563 		curpic = ultra_getpic();
564 
565 		diff = (int64_t)((uint32_t)(curpic & PIC_MASK) -
566 		    pic[i*2]->opl_pic);
567 		if (diff < 0)
568 			diff += (1ll << 32);
569 		*pic_data[i*2] += diff;
570 
571 		diff = (int64_t)((uint32_t)(curpic >> 32) -
572 		    pic[i*2 + 1]->opl_pic);
573 		if (diff < 0)
574 			diff += (1ll << 32);
575 		*pic_data[i*2 + 1] += diff;
576 
577 		pic[i*2]->opl_pic = (uint32_t)(curpic & PIC_MASK);
578 		pic[i*2 + 1]->opl_pic = (uint32_t)(curpic >> 32);
579 	}
580 
581 }
582 
583 static void
584 opl_pcbe_free(void *config)
585 {
586 	kmem_free(config, sizeof (opl_pcbe_config_t));
587 }
588 
589 
590 static struct modlpcbe modlpcbe = {
591 	&mod_pcbeops,
592 	"SPARC64 VI Performance Counters v%I%",
593 	&opl_pcbe_ops
594 };
595 
596 static struct modlinkage modl = {
597 	MODREV_1,
598 	&modlpcbe,
599 };
600 
601 int
602 _init(void)
603 {
604 	if (opl_pcbe_init() != 0)
605 		return (ENOTSUP);
606 	return (mod_install(&modl));
607 }
608 
609 int
610 _fini(void)
611 {
612 	return (mod_remove(&modl));
613 }
614 
615 int
616 _info(struct modinfo *mi)
617 {
618 	return (mod_info(&modl, mi));
619 }
620