xref: /illumos-gate/usr/src/uts/common/io/tvhci.c (revision 35551380472894a564e057962b701af78f719377)
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"
28 
29 /*
30  * The tvhci driver can be used to exercise the mpxio framework together
31  * with tphci/tclient.
32  */
33 
34 #include <sys/conf.h>
35 #include <sys/file.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/scsi/scsi.h>
39 #include <sys/scsi/impl/scsi_reset_notify.h>
40 #include <sys/sunmdi.h>
41 #include <sys/mdi_impldefs.h>
42 #include <sys/disp.h>
43 
44 /* cb_ops entry points */
45 static int tvhci_open(dev_t *, int, int, cred_t *);
46 static int tvhci_close(dev_t, int, int, cred_t *);
47 static int tvhci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
48 static int tvhci_attach(dev_info_t *, ddi_attach_cmd_t);
49 static int tvhci_detach(dev_info_t *, ddi_detach_cmd_t);
50 static int tvhci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
51 
52 /* bus_ops entry points */
53 static int tvhci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
54     void *);
55 static int tvhci_initchild(dev_info_t *, dev_info_t *);
56 static int tvhci_uninitchild(dev_info_t *, dev_info_t *);
57 static int tvhci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *,
58     dev_info_t **);
59 static int tvhci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t,
60     void *);
61 static int tvhci_intr_op(dev_info_t *dip, dev_info_t *rdip,
62     ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result);
63 
64 /* vhci ops */
65 static int tvhci_pi_init(dev_info_t *, mdi_pathinfo_t *, int);
66 static int tvhci_pi_uninit(dev_info_t *, mdi_pathinfo_t *, int);
67 static int tvhci_pi_state_change(dev_info_t *, mdi_pathinfo_t *,
68     mdi_pathinfo_state_t, uint32_t, int);
69 static int tvhci_failover(dev_info_t *, dev_info_t *, int);
70 
71 static void *tvhci_state;
72 struct tvhci_state {
73 	dev_info_t *dip;
74 };
75 
76 static mdi_vhci_ops_t tvhci_opinfo = {
77 	MDI_VHCI_OPS_REV,
78 	tvhci_pi_init,
79 	tvhci_pi_uninit,
80 	tvhci_pi_state_change,
81 	tvhci_failover
82 };
83 
84 static struct cb_ops tvhci_cb_ops = {
85 	tvhci_open,			/* open */
86 	tvhci_close,			/* close */
87 	nodev,				/* strategy */
88 	nodev,				/* print */
89 	nodev,				/* dump */
90 	nodev,				/* read */
91 	nodev,				/* write */
92 	tvhci_ioctl,			/* ioctl */
93 	nodev,				/* devmap */
94 	nodev,				/* mmap */
95 	nodev,				/* segmap */
96 	nochpoll,			/* chpoll */
97 	ddi_prop_op,			/* cb_prop_op */
98 	0,				/* streamtab */
99 	D_NEW | D_MP,			/* cb_flag */
100 	CB_REV,				/* rev */
101 	nodev,				/* aread */
102 	nodev				/* awrite */
103 };
104 
105 static struct bus_ops tvhci_bus_ops = {
106 	BUSO_REV,			/* busops_rev */
107 	nullbusmap,			/* bus_map */
108 	NULL,				/* bus_get_intrspec */
109 	NULL,				/* bus_add_interspec */
110 	NULL,				/* bus_remove_interspec */
111 	i_ddi_map_fault,		/* bus_map_fault */
112 	ddi_no_dma_map,			/* bus_dma_map */
113 	ddi_no_dma_allochdl,		/* bus_dma_allochdl */
114 	NULL,				/* bus_dma_freehdl */
115 	NULL,				/* bus_dma_bindhdl */
116 	NULL,				/* bus_dma_unbindhdl */
117 	NULL,				/* bus_dma_flush */
118 	NULL,				/* bus_dma_win */
119 	NULL,				/* bus_dma_ctl */
120 	tvhci_ctl,			/* bus_ctl */
121 	ddi_bus_prop_op,		/* bus_prop_op */
122 	NULL,				/* bus_get_eventcookie */
123 	NULL,				/* bus_add_eventcall */
124 	NULL,				/* bus_remove_event */
125 	NULL,				/* bus_post_event */
126 	NULL,				/* bus_intr_ctl */
127 	tvhci_bus_config,		/* bus_config */
128 	tvhci_bus_unconfig,		/* bus_unconfig */
129 	NULL,				/* bus_fm_init */
130 	NULL,				/* bus_fm_fini */
131 	NULL,				/* bus_fm_access_enter */
132 	NULL,				/* bus_fm_access_exit */
133 	NULL,				/* bus_power */
134 	tvhci_intr_op			/* bus_intr_op */
135 };
136 
137 static struct dev_ops tvhci_ops = {
138 	DEVO_REV,
139 	0,
140 	tvhci_getinfo,
141 	nulldev,		/* identify */
142 	nulldev,		/* probe */
143 	tvhci_attach,		/* attach and detach are mandatory */
144 	tvhci_detach,
145 	nodev,			/* reset */
146 	&tvhci_cb_ops,		/* cb_ops */
147 	&tvhci_bus_ops,		/* bus_ops */
148 	NULL,			/* power */
149 };
150 
151 extern struct mod_ops mod_driverops;
152 
153 static struct modldrv modldrv = {
154 	&mod_driverops,
155 	"test vhci driver %I%",
156 	&tvhci_ops
157 };
158 
159 static struct modlinkage modlinkage = {
160 	MODREV_1,
161 	&modldrv,
162 	NULL
163 };
164 
165 int
166 _init(void)
167 {
168 	int rval;
169 
170 	if ((rval = ddi_soft_state_init(&tvhci_state,
171 	    sizeof (struct tvhci_state), 2)) != 0) {
172 		return (rval);
173 	}
174 
175 	if ((rval = mod_install(&modlinkage)) != 0) {
176 		ddi_soft_state_fini(&tvhci_state);
177 	}
178 	return (rval);
179 }
180 
181 
182 int
183 _fini(void)
184 {
185 	int rval;
186 
187 	/*
188 	 * don't start cleaning up until we know that the module remove
189 	 * has worked  -- if this works, then we know that each instance
190 	 * has successfully been detached
191 	 */
192 	if ((rval = mod_remove(&modlinkage)) != 0) {
193 		return (rval);
194 	}
195 
196 	ddi_soft_state_fini(&tvhci_state);
197 
198 	return (rval);
199 }
200 
201 int
202 _info(struct modinfo *modinfop)
203 {
204 	return (mod_info(&modlinkage, modinfop));
205 }
206 
207 /* ARGSUSED */
208 static int
209 tvhci_open(dev_t *devp, int flag, int otype, cred_t *credp)
210 {
211 	struct tvhci_state *vhci;
212 
213 	if (otype != OTYP_CHR) {
214 		return (EINVAL);
215 	}
216 
217 	vhci = ddi_get_soft_state(tvhci_state, getminor(*devp));
218 	if (vhci == NULL) {
219 		return (ENXIO);
220 	}
221 
222 	return (0);
223 }
224 
225 
226 /* ARGSUSED */
227 static int
228 tvhci_close(dev_t dev, int flag, int otype, cred_t *credp)
229 {
230 	struct tvhci_state *vhci;
231 	if (otype != OTYP_CHR) {
232 		return (EINVAL);
233 	}
234 
235 	vhci = ddi_get_soft_state(tvhci_state, getminor(dev));
236 	if (vhci == NULL) {
237 		return (ENXIO);
238 	}
239 
240 	return (0);
241 }
242 
243 /* ARGSUSED */
244 static int
245 tvhci_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
246 	cred_t *credp, int *rval)
247 {
248 	return (0);
249 }
250 
251 /*
252  * attach the module
253  */
254 static int
255 tvhci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
256 {
257 	char *vclass;
258 	int instance, vhci_regis = 0;
259 	struct tvhci_state *vhci = NULL;
260 	dev_info_t *pdip;
261 
262 	instance = ddi_get_instance(dip);
263 
264 	switch (cmd) {
265 	case DDI_ATTACH:
266 		break;
267 
268 	case DDI_RESUME:
269 	case DDI_PM_RESUME:
270 		return (0);	/* nothing to do */
271 
272 	default:
273 		return (DDI_FAILURE);
274 	}
275 
276 	/*
277 	 * Allocate vhci data structure.
278 	 */
279 	if (ddi_soft_state_zalloc(tvhci_state, instance) != DDI_SUCCESS) {
280 		return (DDI_FAILURE);
281 	}
282 
283 	vhci = ddi_get_soft_state(tvhci_state, instance);
284 	ASSERT(vhci != NULL);
285 	vhci->dip = dip;
286 
287 	/* parent must be /pshot */
288 	pdip = ddi_get_parent(dip);
289 	if (strcmp(ddi_driver_name(pdip), "pshot") != 0 ||
290 	    ddi_get_parent(pdip) != ddi_root_node()) {
291 		cmn_err(CE_NOTE, "tvhci must be under /pshot/");
292 		goto attach_fail;
293 	}
294 
295 	/*
296 	 * XXX add mpxio-disable property. need to remove the check
297 	 *	from the framework
298 	 */
299 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
300 	    "mpxio-disable", "no");
301 
302 	/* bus_addr is the <vhci_class> */
303 	vclass = ddi_get_name_addr(dip);
304 	if (vclass == NULL || vclass[1] == '\0') {
305 		cmn_err(CE_NOTE, "tvhci invalid vhci class");
306 		goto attach_fail;
307 	}
308 
309 	/*
310 	 * Attach this instance with the mpxio framework
311 	 */
312 	if (mdi_vhci_register(vclass, dip, &tvhci_opinfo, 0) != MDI_SUCCESS) {
313 		cmn_err(CE_WARN, "%s mdi_vhci_register failed",
314 		    ddi_node_name(dip));
315 		goto attach_fail;
316 	}
317 	vhci_regis++;
318 
319 	if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
320 	    instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) {
321 		cmn_err(CE_NOTE, "%s ddi_create_minor_node failed",
322 		    ddi_node_name(dip));
323 		goto attach_fail;
324 	}
325 
326 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1);
327 	ddi_report_dev(dip);
328 	return (DDI_SUCCESS);
329 
330 attach_fail:
331 	if (vhci_regis)
332 		(void) mdi_vhci_unregister(dip, 0);
333 
334 	ddi_soft_state_free(tvhci_state, instance);
335 	return (DDI_FAILURE);
336 }
337 
338 
339 /*ARGSUSED*/
340 static int
341 tvhci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
342 {
343 	int instance = ddi_get_instance(dip);
344 
345 	switch (cmd) {
346 	case DDI_DETACH:
347 		break;
348 
349 	case DDI_SUSPEND:
350 	case DDI_PM_SUSPEND:
351 		return (0);	/* nothing to do */
352 
353 	default:
354 		return (DDI_FAILURE);
355 	}
356 
357 	if (mdi_vhci_unregister(dip, 0) != MDI_SUCCESS)
358 		return (DDI_FAILURE);
359 
360 	ddi_remove_minor_node(dip, NULL);
361 	ddi_soft_state_free(tvhci_state, instance);
362 
363 	return (DDI_SUCCESS);
364 }
365 
366 /*
367  * tvhci_getinfo()
368  * Given the device number, return the devinfo pointer or the
369  * instance number.
370  * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach.
371  */
372 
373 /*ARGSUSED*/
374 static int
375 tvhci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
376 {
377 	struct tvhci_state *vhci;
378 	int instance = getminor((dev_t)arg);
379 
380 	switch (cmd) {
381 	case DDI_INFO_DEVT2DEVINFO:
382 		vhci = ddi_get_soft_state(tvhci_state, instance);
383 		if (vhci != NULL)
384 			*result = vhci->dip;
385 		else {
386 			*result = NULL;
387 			return (DDI_FAILURE);
388 		}
389 		break;
390 
391 	case DDI_INFO_DEVT2INSTANCE:
392 		*result = (void *)(uintptr_t)instance;
393 		break;
394 
395 	default:
396 		return (DDI_FAILURE);
397 	}
398 
399 	return (DDI_SUCCESS);
400 }
401 
402 /*ARGSUSED*/
403 static int
404 tvhci_pi_init(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
405 {
406 	return (MDI_SUCCESS);
407 }
408 
409 /*ARGSUSED*/
410 static int
411 tvhci_pi_uninit(dev_info_t *vdip, mdi_pathinfo_t *pip, int flags)
412 {
413 	return (MDI_SUCCESS);
414 }
415 
416 /*ARGSUSED*/
417 static int
418 tvhci_pi_state_change(dev_info_t *vdip, mdi_pathinfo_t *pip,
419     mdi_pathinfo_state_t state, uint32_t ext_state, int flags)
420 {
421 	return (MDI_SUCCESS);
422 }
423 
424 /*ARGSUSED*/
425 static int
426 tvhci_failover(dev_info_t *vdip, dev_info_t *cdip, int flags)
427 {
428 	return (MDI_SUCCESS);
429 }
430 
431 /*
432  * Interrupt stuff. NO OP for pseudo drivers.
433  */
434 /*ARGSUSED*/
435 static int
436 tvhci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
437     ddi_intr_handle_impl_t *hdlp, void *result)
438 {
439 	return (DDI_FAILURE);
440 }
441 
442 /*ARGSUSED*/
443 static int
444 tvhci_ctl(dev_info_t *dip, dev_info_t *rdip,
445 	ddi_ctl_enum_t ctlop, void *arg, void *result)
446 {
447 	switch (ctlop) {
448 	case DDI_CTLOPS_REPORTDEV:
449 		if (rdip == (dev_info_t *)0)
450 			return (DDI_FAILURE);
451 		cmn_err(CE_CONT, "?tvhci-device: %s%d\n",
452 		    ddi_get_name(rdip), ddi_get_instance(rdip));
453 		return (DDI_SUCCESS);
454 
455 	case DDI_CTLOPS_INITCHILD:
456 	{
457 		dev_info_t *child = (dev_info_t *)arg;
458 		return (tvhci_initchild(dip, child));
459 	}
460 
461 	case DDI_CTLOPS_UNINITCHILD:
462 	{
463 		dev_info_t *child = (dev_info_t *)arg;
464 		return (tvhci_uninitchild(dip, child));
465 	}
466 
467 	case DDI_CTLOPS_DMAPMAPC:
468 	case DDI_CTLOPS_REPORTINT:
469 	case DDI_CTLOPS_REGSIZE:
470 	case DDI_CTLOPS_NREGS:
471 	case DDI_CTLOPS_SIDDEV:
472 	case DDI_CTLOPS_SLAVEONLY:
473 	case DDI_CTLOPS_AFFINITY:
474 	case DDI_CTLOPS_POKE:
475 	case DDI_CTLOPS_PEEK:
476 		/*
477 		 * These ops correspond to functions that "shouldn't" be called
478 		 * by a pseudo driver.  So we whine when we're called.
479 		 */
480 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
481 			ddi_get_name(dip), ddi_get_instance(dip),
482 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
483 		return (DDI_FAILURE);
484 
485 	case DDI_CTLOPS_ATTACH:
486 	case DDI_CTLOPS_BTOP:
487 	case DDI_CTLOPS_BTOPR:
488 	case DDI_CTLOPS_DETACH:
489 	case DDI_CTLOPS_DVMAPAGESIZE:
490 	case DDI_CTLOPS_IOMIN:
491 	case DDI_CTLOPS_POWER:
492 	case DDI_CTLOPS_PTOB:
493 	default:
494 		/*
495 		 * The ops that we pass up (default).  We pass up memory
496 		 * allocation oriented ops that we receive - these may be
497 		 * associated with pseudo HBA drivers below us with target
498 		 * drivers below them that use ddi memory allocation
499 		 * interfaces like scsi_alloc_consistent_buf.
500 		 */
501 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
502 	}
503 }
504 
505 /* set devi_addr to "g<guid>" */
506 static int
507 tvhci_initchild(dev_info_t *dip, dev_info_t *child)
508 {
509 	_NOTE(ARGUNUSED(dip))
510 	char *guid, *addr;
511 
512 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
513 	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
514 		cmn_err(CE_NOTE, "tvhci_initchild - no guid property");
515 		return (DDI_FAILURE);
516 	}
517 
518 	addr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
519 	(void) snprintf(addr, MAXNAMELEN, "g%s", guid);
520 	ddi_set_name_addr(child, addr);
521 
522 	kmem_free(addr, MAXNAMELEN);
523 	ddi_prop_free(guid);
524 	return (DDI_SUCCESS);
525 }
526 
527 /*ARGSUSED*/
528 static int
529 tvhci_uninitchild(dev_info_t *dip, dev_info_t *child)
530 {
531 	ddi_set_name_addr(child, NULL);
532 	return (DDI_SUCCESS);
533 }
534 
535 /* form paddr by cname@<phci_inst>,<guid> */
536 static char *
537 tvh_get_phci_devname(char *cname, char *guid,
538     dev_info_t *pdip, char *pname, int len)
539 {
540 	(void) snprintf(pname, len, "%s@%d,%s",
541 	    cname, ddi_get_instance(pdip), guid);
542 	return (pname);
543 }
544 
545 static int
546 tvh_enum_by_phci(dev_info_t *vdip, char *devnm, int flags)
547 {
548 	mdi_phci_t *ph;
549 	mdi_vhci_t *vh = (mdi_vhci_t *)DEVI(vdip)->devi_mdi_xhci;
550 	dev_info_t *pdip, *cdip;
551 	char *cname, *caddr, *guid, *pname;
552 	int rval = DDI_FAILURE;
553 
554 	(void) i_ddi_parse_name(devnm, &cname, &caddr, NULL);
555 	if (cname == NULL || caddr == NULL || caddr[0] != 'g')
556 		return (rval);
557 
558 	guid = caddr + 1;
559 	pname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
560 
561 	/* mutex_enter(&mdi_mutex); XXX need lock access */
562 	ph = vh->vh_phci_head;
563 	while (ph) {
564 		pdip = ph->ph_dip;
565 		/* mutex_exit(&mdi_mutex); */
566 		(void) tvh_get_phci_devname(cname, guid, pdip, pname,
567 		    MAXNAMELEN);
568 		if (ndi_devi_config_one(pdip, pname, &cdip, flags)
569 		    == DDI_SUCCESS) {
570 			ndi_rele_devi(cdip);
571 			rval = DDI_SUCCESS;
572 		}
573 		/* mutex_enter(&mdi_mutex); */
574 		ph = ph->ph_next;
575 	}
576 	/* mutex_exit(&mdi_mutex); */
577 
578 	*(caddr - 1) = '@';	/* undo damage from i_ddi_parse_name() */
579 	kmem_free(pname, MAXNAMELEN);
580 	return (rval);
581 }
582 
583 static int
584 tvh_remove_by_phci(dev_info_t *vdip, char *devnm, int flags)
585 {
586 	int rval = DDI_SUCCESS;
587 	mdi_phci_t *ph;
588 	mdi_vhci_t *vh = (mdi_vhci_t *)DEVI(vdip)->devi_mdi_xhci;
589 	dev_info_t *pdip;
590 	char *cname, *caddr, *guid, *pname;
591 
592 	(void) i_ddi_parse_name(devnm, &cname, &caddr, NULL);
593 	if (cname == NULL || caddr == NULL || caddr[0] != 'g')
594 		return (rval);	/* devnm can't exist */
595 
596 	guid = caddr + 1;
597 	pname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
598 
599 	/* mutex_enter(&mdi_mutex); XXX need lock access */
600 	ph = vh->vh_phci_head;
601 	while (ph) {
602 		pdip = ph->ph_dip;
603 		/* mutex_exit(&mdi_mutex); */
604 		(void) tvh_get_phci_devname(cname, guid, pdip, pname,
605 		    MAXNAMELEN);
606 		rval = ndi_devi_unconfig_one(pdip, pname, NULL, flags);
607 		/* mutex_enter(&mdi_mutex); */
608 		if (rval != NDI_SUCCESS)
609 			break;
610 		ph = ph->ph_next;
611 	}
612 	/* mutex_exit(&mdi_mutex); */
613 
614 	kmem_free(pname, MAXNAMELEN);
615 	return (rval);
616 }
617 
618 static int
619 tvhci_bus_config(dev_info_t *parent, uint_t flags,
620     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
621 {
622 	char *devnm;
623 	dev_info_t *cdip;
624 	int circ;
625 
626 	switch (op) {
627 	case BUS_CONFIG_ONE:
628 		break;
629 	case BUS_CONFIG_ALL:
630 		/* XXX call into phci's here? */
631 	case BUS_CONFIG_DRIVER:
632 		return (ndi_busop_bus_config(parent, flags, op, arg, childp,
633 		    0));
634 	default:
635 		return (DDI_FAILURE);
636 	}
637 
638 	devnm = (char *)arg;
639 	ndi_devi_enter(parent, &circ);
640 	cdip = ndi_devi_findchild(parent, devnm);
641 	ndi_devi_exit(parent, circ);
642 	if (cdip == NULL) {
643 		/* call into registered phci's */
644 		(void) tvh_enum_by_phci(parent, devnm, flags);
645 	}
646 
647 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
648 }
649 
650 static int
651 tvhci_bus_unconfig(dev_info_t *parent, uint_t flags,
652     ddi_bus_config_op_t op, void *arg)
653 {
654 	char *devnm, *slashname;
655 	int rval, circ;
656 	dev_info_t *cdip, *ndip;
657 
658 	/*
659 	 * If we are not removing device nodes, pathinfo can be
660 	 * left as is. So no need to disturb the phci's.
661 	 */
662 	if ((flags & NDI_DEVI_REMOVE) == 0) {
663 		return (ndi_busop_bus_unconfig(parent, flags, op, arg));
664 	}
665 
666 	switch (op) {
667 	case BUS_UNCONFIG_ONE:
668 		devnm = (char *)arg;
669 		ndi_devi_enter(parent, &circ);
670 		if (ndi_devi_findchild(parent, devnm) == NULL) {
671 			ndi_devi_exit(parent, circ);
672 			return (DDI_SUCCESS);
673 		}
674 		ndi_devi_exit(parent, circ);
675 		return (tvh_remove_by_phci(parent, devnm, flags));
676 
677 	case BUS_UNCONFIG_ALL:
678 		/* this functionality is for developers only */
679 		rval = DDI_SUCCESS;
680 		slashname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
681 		devnm = slashname + 1;
682 		ndi_devi_enter(parent, &circ);
683 		cdip = ddi_get_child(parent);
684 		while (cdip != NULL) {
685 			ndip = ddi_get_next_sibling(cdip);
686 			(void) ddi_deviname(cdip, slashname);
687 			ndi_devi_exit(parent, circ);
688 			rval = tvh_remove_by_phci(parent, devnm, flags);
689 			if (rval != DDI_SUCCESS) {
690 				break;
691 			}
692 			ndi_devi_enter(parent, &circ);
693 			cdip = ndip;
694 		}
695 		ndi_devi_exit(parent, circ);
696 		return (rval);
697 
698 	case BUS_UNCONFIG_DRIVER:
699 		/* unconfig driver never comes with NDI_DEVI_REMOVE */
700 	default:
701 		return (DDI_FAILURE);
702 	}
703 	/*NOTREACHED*/
704 }
705