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