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