xref: /titanic_41/usr/src/uts/sun4v/io/vnex.c (revision 74e20cfe817b82802b16fac8690dadcda76f54f5)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4 5.0 */
27 
28 #include <sys/types.h>
29 #include <sys/cmn_err.h>
30 #include <sys/conf.h>
31 #include <sys/ddi_impldefs.h>
32 #include <sys/autoconf.h>
33 #include <sys/systm.h>
34 #include <sys/modctl.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/ddi_subrdefs.h>
38 #include <sys/promif.h>
39 #include <sys/machsystm.h>
40 #include <sys/ddi_intr_impl.h>
41 #include <sys/hypervisor_api.h>
42 #include <sys/intr.h>
43 
44 #define	SUN4V_REG_SPEC2CFG_HDL(x)	((x >> 32) & ~(0xfull << 28))
45 
46 static kmutex_t vnex_id_lock;
47 /*
48  * Vnex name  to pil map
49  */
50 typedef struct vnex_regspec {
51 	uint64_t physaddr;
52 	uint64_t size;
53 } vnex_regspec_t;
54 
55 struct vnex_pil_map {
56 	caddr_t	name;
57 	uint32_t pil;
58 };
59 
60 /* vnex interrupt descriptor */
61 typedef struct vnex_id {
62 	dev_info_t *vid_dip;
63 	uint32_t vid_ino;
64 	uint64_t vid_ihdl;
65 	uint_t	(*vid_handler)();
66 	caddr_t	vid_arg1;
67 	caddr_t	vid_arg2;
68 	ddi_intr_handle_impl_t *vid_ddi_hdlp;
69 	uint64_t vid_cfg_hdl;
70 	struct vnex_id *vid_next;
71 } vnex_id_t;
72 
73 /* vnex interrupt descriptor list */
74 static vnex_id_t *vnex_id_list;
75 
76 hrtime_t vnex_pending_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */
77 
78 /*
79  * vnex interrupt descriptor list manipulation functions
80  */
81 
82 static vnex_id_t *vnex_locate_id(dev_info_t *dip, uint32_t ino);
83 static vnex_id_t *vnex_alloc_id(dev_info_t *dip, uint32_t ino,
84 	uint64_t dhdl);
85 static void vnex_add_id(vnex_id_t *vid_p);
86 static void vnex_rem_id(vnex_id_t *vid_p);
87 static void vnex_free_id(vnex_id_t *vid_p);
88 
89 uint_t vnex_intr_wrapper(caddr_t arg);
90 
91 static struct vnex_pil_map vnex_name_to_pil[] = {
92 	{"console", 	PIL_12},
93 	{"fma",		PIL_5},
94 	{"echo", 	PIL_3},
95 	{"loop", 	PIL_3},
96 	{"sunmc", 	PIL_3},
97 	{"sunvts", 	PIL_3},
98 	{"explorer", 	PIL_3},
99 	{"ncp", 	PIL_8},
100 	{"crypto", 	PIL_8}
101 };
102 
103 #define	VNEX_MAX_DEVS	(sizeof (vnex_name_to_pil) /	\
104 			    sizeof (struct vnex_pil_map))
105 
106 /*
107  * Config information
108  */
109 static int vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
110     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
111 
112 static int
113 vnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
114 
115 static struct bus_ops vnex_bus_ops = {
116 	BUSO_REV,
117 	nullbusmap,
118 	NULL,	/* NO OP */
119 	NULL,	/* NO OP */
120 	NULL,	/* NO OP */
121 	i_ddi_map_fault,
122 	ddi_no_dma_map,
123 	ddi_no_dma_allochdl,
124 	NULL,
125 	NULL,
126 	NULL,
127 	NULL,
128 	NULL,
129 	NULL,
130 	vnex_ctl,
131 	ddi_bus_prop_op,
132 	NULL,	/* (*bus_get_eventcookie)();    */
133 	NULL,	/* (*bus_add_eventcall)();	*/
134 	NULL,	/* (*bus_remove_eventcall)();   */
135 	NULL,	/* (*bus_post_event)();		*/
136 	NULL,	/* (*bus_intr_ctl)();		*/
137 	NULL,	/* (*bus_config)();		*/
138 	NULL,	/* (*bus_unconfig)();		*/
139 	NULL,	/* (*bus_fm_init)();		*/
140 	NULL,	/* (*bus_fm_fini)();		*/
141 	NULL,	/* (*bus_fm_access_enter)();	*/
142 	NULL,	/* (*bus_fm_access_fini)();	*/
143 	NULL,	/* (*bus_power)();		*/
144 	vnex_intr_ops	/* (*bus_intr_op)();	*/
145 };
146 
147 static int vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
148 static int vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
149 
150 static struct dev_ops pseudo_ops = {
151 	DEVO_REV,		/* devo_rev, */
152 	0,			/* refcnt  */
153 	ddi_no_info,		/* info */
154 	nulldev,		/* identify */
155 	nulldev,		/* probe */
156 	vnex_attach,		/* attach */
157 	vnex_detach,		/* detach */
158 	nodev,			/* reset */
159 	(struct cb_ops *)0,	/* driver operations */
160 	&vnex_bus_ops,	/* bus operations */
161 	nulldev			/* power */
162 };
163 
164 /*
165  * Module linkage information for the kernel.
166  */
167 
168 static struct modldrv modldrv = {
169 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
170 	"sun4v virtual-devices nexus driver v%I%",
171 	&pseudo_ops,	/* driver ops */
172 };
173 
174 static struct modlinkage modlinkage = {
175 	MODREV_1, (void *)&modldrv, NULL
176 };
177 
178 int
179 _init(void)
180 {
181 	return (mod_install(&modlinkage));
182 }
183 
184 int
185 _fini(void)
186 {
187 	return (mod_remove(&modlinkage));
188 }
189 
190 int
191 _info(struct modinfo *modinfop)
192 {
193 	return (mod_info(&modlinkage, modinfop));
194 }
195 
196 /*ARGSUSED*/
197 void
198 vnex_intr_dist(void *arg)
199 {
200 	vnex_id_t *vid_p;
201 	uint32_t cpuid;
202 	int	intr_state;
203 	hrtime_t start;
204 
205 	mutex_enter(&vnex_id_lock);
206 
207 	for (vid_p = vnex_id_list; vid_p != NULL;
208 	    vid_p = vid_p->vid_next) {
209 		/*
210 		 * Don't do anything for disabled interrupts.
211 		 * vnex_enable_intr takes care of redistributing interrupts.
212 		 */
213 		if ((hvio_intr_getvalid(vid_p->vid_ihdl,
214 		    &intr_state) == H_EOK) && (intr_state == HV_INTR_NOTVALID))
215 				continue;
216 
217 		cpuid = intr_dist_cpuid();
218 
219 		(void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID);
220 		/*
221 		 * Make a best effort to wait for pending interrupts to finish.
222 		 * There is not much we can do if we timeout.
223 		 */
224 		start = gethrtime();
225 		while (!panicstr &&
226 		    (hvio_intr_getstate(vid_p->vid_ihdl, &intr_state) ==
227 		    H_EOK) && (intr_state == HV_INTR_DELIVERED_STATE)) {
228 			if (gethrtime() - start > vnex_pending_timeout) {
229 				cmn_err(CE_WARN, "vnex_intr_dist: %s%d "
230 				    "ino 0x%x pending: timedout\n",
231 				    ddi_driver_name(vid_p->vid_dip),
232 				    ddi_get_instance(vid_p->vid_dip),
233 				    vid_p->vid_ino);
234 				break;
235 			}
236 		}
237 		(void) hvio_intr_settarget(vid_p->vid_ihdl, cpuid);
238 		(void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID);
239 	}
240 	mutex_exit(&vnex_id_lock);
241 }
242 
243 static int
244 vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
245 {
246 	switch (cmd) {
247 	case DDI_ATTACH:
248 		/*
249 		 * Intitialize interrupt descriptor list
250 		 * and mutex.
251 		 */
252 		vnex_id_list = NULL;
253 		mutex_init(&vnex_id_lock, NULL, MUTEX_DRIVER, NULL);
254 		/*
255 		 * Add interrupt redistribution callback.
256 		 */
257 		intr_dist_add(vnex_intr_dist, dip);
258 		return (DDI_SUCCESS);
259 
260 	case DDI_RESUME:
261 		return (DDI_SUCCESS);
262 
263 	default:
264 		return (DDI_FAILURE);
265 	}
266 }
267 
268 /*ARGSUSED*/
269 static int
270 vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
271 {
272 	switch (cmd) {
273 	case DDI_DETACH:
274 		return (DDI_FAILURE);
275 
276 	case DDI_SUSPEND:
277 		return (DDI_SUCCESS);
278 
279 	default:
280 		return (DDI_FAILURE);
281 	}
282 }
283 
284 static int
285 vnex_ctl(dev_info_t *dip, dev_info_t *rdip,
286 	ddi_ctl_enum_t ctlop, void *arg, void *result)
287 {
288 	char	name[12];	/* enough for a decimal integer */
289 	int		reglen;
290 	uint32_t	*vnex_regspec;
291 
292 	switch (ctlop) {
293 	case DDI_CTLOPS_REPORTDEV:
294 		if (rdip == NULL)
295 			return (DDI_FAILURE);
296 		cmn_err(CE_CONT, "?virtual-device: %s%d\n",
297 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
298 		return (DDI_SUCCESS);
299 
300 	case DDI_CTLOPS_INITCHILD:
301 	{
302 		dev_info_t *child = (dev_info_t *)arg;
303 
304 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
305 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
306 			return (DDI_FAILURE);
307 
308 		(void) sprintf(name, "%x", *vnex_regspec);
309 		ddi_set_name_addr(child, name);
310 		ddi_set_parent_data(child, NULL);
311 		kmem_free((caddr_t)vnex_regspec, reglen);
312 		return (DDI_SUCCESS);
313 
314 	}
315 
316 	case DDI_CTLOPS_UNINITCHILD:
317 	{
318 		dev_info_t *child = (dev_info_t *)arg;
319 
320 		ddi_set_name_addr(child, NULL);
321 		ddi_remove_minor_node(arg, NULL);
322 		return (DDI_SUCCESS);
323 	}
324 
325 	/*
326 	 * These ops correspond to functions that "shouldn't" be called
327 	 * by a pseudo driver.  So we whinge when we're called.
328 	 */
329 	case DDI_CTLOPS_DMAPMAPC:
330 	case DDI_CTLOPS_REPORTINT:
331 	case DDI_CTLOPS_REGSIZE:
332 	{
333 		*((off_t *)result) = 0;
334 		return (DDI_SUCCESS);
335 	}
336 	case DDI_CTLOPS_NREGS:
337 	{
338 		dev_info_t *child = (dev_info_t *)arg;
339 		if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
340 		    "reg", (caddr_t)&vnex_regspec, &reglen) != DDI_SUCCESS)
341 			return (DDI_FAILURE);
342 		*((uint_t *)result) = reglen / sizeof (uint32_t);
343 		kmem_free((caddr_t)vnex_regspec, reglen);
344 		return (DDI_SUCCESS);
345 	}
346 	case DDI_CTLOPS_SIDDEV:
347 	case DDI_CTLOPS_SLAVEONLY:
348 	case DDI_CTLOPS_AFFINITY:
349 	case DDI_CTLOPS_IOMIN:
350 	case DDI_CTLOPS_POKE:
351 	case DDI_CTLOPS_PEEK:
352 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
353 			ddi_get_name(dip), ddi_get_instance(dip),
354 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
355 		return (DDI_FAILURE);
356 
357 	/*
358 	 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
359 	 */
360 	default:
361 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
362 	}
363 }
364 
365 static int
366 vnex_get_pil(dev_info_t *rdip)
367 {
368 	int i;
369 	caddr_t	name;
370 
371 	name = ddi_node_name(rdip);
372 	for (i = 0; i < VNEX_MAX_DEVS; i++) {
373 		if (strcmp(vnex_name_to_pil[i].name,
374 		    name) == 0) {
375 			return (vnex_name_to_pil[i].pil);
376 		}
377 	}
378 	/*
379 	 * if not found pil is 0
380 	 */
381 	return (0);
382 }
383 
384 static int
385 vnex_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
386 {
387 	vnex_id_t *vid_p;
388 	uint32_t cpuid;
389 
390 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
391 
392 	ASSERT(vid_p != NULL);
393 
394 	cpuid = intr_dist_cpuid();
395 
396 	if ((hvio_intr_settarget(vid_p->vid_ihdl, cpuid)) != H_EOK) {
397 		return (DDI_FAILURE);
398 	}
399 
400 	if (hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE) != H_EOK) {
401 		return (DDI_FAILURE);
402 	}
403 
404 	if ((hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID)) != H_EOK) {
405 		return (DDI_FAILURE);
406 	}
407 
408 	return (DDI_SUCCESS);
409 }
410 
411 static int
412 vnex_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
413 {
414 	vnex_id_t *vid_p;
415 
416 	vid_p = vnex_locate_id(rdip, hdlp->ih_vector);
417 
418 	ASSERT(vid_p != NULL);
419 
420 	if (hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID) != H_EOK) {
421 		return (DDI_FAILURE);
422 	}
423 
424 	return (DDI_SUCCESS);
425 }
426 
427 int
428 vnex_ino_to_inum(dev_info_t *dip, uint32_t ino)
429 {
430 	vnex_id_t		*vid_p;
431 	ddi_intr_handle_impl_t	*hdlp;
432 
433 	if ((vid_p = vnex_locate_id(dip, ino)) == NULL)
434 		return (-1);
435 	else if ((hdlp = vid_p->vid_ddi_hdlp) == NULL)
436 		return (-1);
437 	else
438 		return (hdlp->ih_inum);
439 }
440 
441 static int
442 vnex_add_intr(dev_info_t *dip, dev_info_t *rdip,
443     ddi_intr_handle_impl_t *hdlp)
444 {
445 	int reglen, ret = DDI_SUCCESS;
446 	vnex_id_t	*vid_p;
447 	uint64_t cfg;
448 	uint32_t ino;
449 	uint64_t ihdl;
450 	vnex_regspec_t *reg_p;
451 
452 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
453 	    DDI_PROP_DONTPASS, "reg", (caddr_t)&reg_p,
454 	    &reglen) != DDI_SUCCESS) {
455 		return (DDI_FAILURE);
456 	}
457 
458 	/*
459 	 * get the sun4v config handle for this device
460 	 */
461 
462 	cfg = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
463 	kmem_free(reg_p, reglen);
464 	ino = hdlp->ih_vector;
465 
466 	/*
467 	 * call hv to get vihdl
468 	 */
469 	if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK)
470 		return (DDI_FAILURE);
471 
472 	hdlp->ih_vector = ihdl;
473 	/*
474 	 * Allocate a interrupt descriptor (id) with the
475 	 * the interrupt handler and append it to
476 	 * the id list.
477 	 */
478 
479 	vid_p = vnex_alloc_id(rdip, ino, cfg);
480 	vid_p->vid_ihdl = ihdl;
481 	vid_p->vid_handler =  hdlp->ih_cb_func;
482 	vid_p->vid_arg1 =  hdlp->ih_cb_arg1;
483 	vid_p->vid_arg2 =  hdlp->ih_cb_arg2;
484 	vid_p->vid_ddi_hdlp =  hdlp;
485 
486 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
487 	    (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL);
488 
489 	if (hdlp->ih_pri == 0) {
490 		hdlp->ih_pri = vnex_get_pil(rdip);
491 	}
492 
493 	ret = i_ddi_add_ivintr(hdlp);
494 	if (ret != DDI_SUCCESS) {
495 		return (ret);
496 	}
497 
498 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler,
499 	    vid_p->vid_arg1, vid_p->vid_arg2);
500 
501 	return (ret);
502 }
503 
504 static int
505 vnex_remove_intr(dev_info_t *rdip,
506 	ddi_intr_handle_impl_t *hdlp)
507 {
508 	vnex_id_t *vid_p;
509 	uint32_t ino;
510 	int ret = DDI_SUCCESS;
511 
512 	ino = hdlp->ih_vector;
513 	vid_p = vnex_locate_id(rdip, ino);
514 
515 	hdlp->ih_vector = vid_p->vid_ihdl;
516 	i_ddi_rem_ivintr(hdlp);
517 
518 	vnex_free_id(vid_p);
519 
520 	return (ret);
521 }
522 
523 static int
524 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip,
525     ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result)
526 {
527 	int	ret = DDI_SUCCESS;
528 
529 	switch (intr_op) {
530 		case DDI_INTROP_GETCAP:
531 			*(int *)result = DDI_INTR_FLAG_LEVEL;
532 			break;
533 		case DDI_INTROP_ALLOC:
534 			*(int *)result = hdlp->ih_scratch1;
535 			break;
536 		case DDI_INTROP_GETPRI:
537 			*(int *)result = hdlp->ih_pri ?
538 			    hdlp->ih_pri : vnex_get_pil(rdip);
539 			break;
540 		case DDI_INTROP_FREE:
541 			break;
542 		case DDI_INTROP_SETPRI:
543 			break;
544 		case DDI_INTROP_ADDISR:
545 			ret = vnex_add_intr(dip, rdip, hdlp);
546 			break;
547 		case DDI_INTROP_REMISR:
548 			ret = vnex_remove_intr(rdip, hdlp);
549 			break;
550 		case DDI_INTROP_ENABLE:
551 			ret = vnex_enable_intr(rdip, hdlp);
552 			break;
553 		case DDI_INTROP_DISABLE:
554 			ret = vnex_disable_intr(rdip, hdlp);
555 			break;
556 		case DDI_INTROP_NINTRS:
557 		case DDI_INTROP_NAVAIL:
558 			*(int *)result = i_ddi_get_intx_nintrs(rdip);
559 			break;
560 		case DDI_INTROP_SUPPORTED_TYPES:
561 			*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
562 			    DDI_INTR_TYPE_FIXED : 0;
563 			break;
564 		default:
565 			ret = DDI_ENOTSUP;
566 			break;
567 	}
568 
569 	return (ret);
570 }
571 
572 vnex_id_t *
573 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl)
574 {
575 	vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP);
576 
577 	vid_p->vid_dip = dip;
578 	vid_p->vid_ino = ino;
579 	vid_p->vid_cfg_hdl = dhdl;
580 
581 	mutex_enter(&vnex_id_lock);
582 	vnex_add_id(vid_p);
583 	mutex_exit(&vnex_id_lock);
584 
585 	return (vid_p);
586 }
587 
588 vnex_id_t *
589 vnex_locate_id(dev_info_t *dip, uint32_t ino)
590 {
591 	vnex_id_t *vid_p;
592 
593 	mutex_enter(&vnex_id_lock);
594 	vid_p = vnex_id_list;
595 
596 	while (vid_p != NULL) {
597 		if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) {
598 			mutex_exit(&vnex_id_lock);
599 			return (vid_p);
600 		}
601 		vid_p = vid_p->vid_next;
602 	}
603 	mutex_exit(&vnex_id_lock);
604 	return (NULL);
605 }
606 
607 static void
608 vnex_free_id(vnex_id_t *vid_p)
609 {
610 	mutex_enter(&vnex_id_lock);
611 	vnex_rem_id(vid_p);
612 	mutex_exit(&vnex_id_lock);
613 
614 	kmem_free(vid_p, sizeof (*vid_p));
615 }
616 
617 static void
618 vnex_rem_id(vnex_id_t *vid_p)
619 {
620 	vnex_id_t *prev_p = vnex_id_list;
621 
622 	if (vnex_id_list == NULL)
623 		cmn_err(CE_PANIC, "vnex: interrupt list empty");
624 
625 	if (vid_p == NULL)
626 		cmn_err(CE_PANIC, "vnex: no element to remove");
627 
628 	if (vnex_id_list == vid_p) {
629 		vnex_id_list = vid_p->vid_next;
630 	} else {
631 		while (prev_p != NULL && prev_p->vid_next != vid_p)
632 			prev_p = prev_p->vid_next;
633 
634 		if (prev_p == NULL)
635 			cmn_err(CE_PANIC, "vnex: element %p not in list",
636 			    (void *) vid_p);
637 
638 		prev_p->vid_next = vid_p->vid_next;
639 	}
640 }
641 
642 static void
643 vnex_add_id(vnex_id_t *vid_p)
644 {
645 	vid_p->vid_next = vnex_id_list;
646 	vnex_id_list = vid_p;
647 }
648 
649 uint_t
650 vnex_intr_wrapper(caddr_t arg)
651 {
652 	vnex_id_t *vid_p = (vnex_id_t *)arg;
653 	int res;
654 	uint_t (*handler)();
655 	caddr_t handler_arg1;
656 	caddr_t handler_arg2;
657 
658 	handler = vid_p->vid_handler;
659 	handler_arg1 = vid_p->vid_arg1;
660 	handler_arg2 = vid_p->vid_arg2;
661 
662 	res = (*handler)(handler_arg1, handler_arg2);
663 
664 	(void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE);
665 
666 	return (res);
667 }
668