xref: /titanic_44/usr/src/uts/common/io/tvhci.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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_NINTRS:
472 	case DDI_CTLOPS_SIDDEV:
473 	case DDI_CTLOPS_SLAVEONLY:
474 	case DDI_CTLOPS_AFFINITY:
475 	case DDI_CTLOPS_INTR_HILEVEL:
476 	case DDI_CTLOPS_XLATE_INTRS:
477 	case DDI_CTLOPS_POKE:
478 	case DDI_CTLOPS_PEEK:
479 		/*
480 		 * These ops correspond to functions that "shouldn't" be called
481 		 * by a pseudo driver.  So we whine when we're called.
482 		 */
483 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
484 			ddi_get_name(dip), ddi_get_instance(dip),
485 			ctlop, ddi_get_name(rdip), ddi_get_instance(rdip));
486 		return (DDI_FAILURE);
487 
488 	case DDI_CTLOPS_ATTACH:
489 	case DDI_CTLOPS_BTOP:
490 	case DDI_CTLOPS_BTOPR:
491 	case DDI_CTLOPS_DETACH:
492 	case DDI_CTLOPS_DVMAPAGESIZE:
493 	case DDI_CTLOPS_IOMIN:
494 	case DDI_CTLOPS_POWER:
495 	case DDI_CTLOPS_PTOB:
496 	default:
497 		/*
498 		 * The ops that we pass up (default).  We pass up memory
499 		 * allocation oriented ops that we receive - these may be
500 		 * associated with pseudo HBA drivers below us with target
501 		 * drivers below them that use ddi memory allocation
502 		 * interfaces like scsi_alloc_consistent_buf.
503 		 */
504 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
505 	}
506 }
507 
508 /* set devi_addr to "g<guid>" */
509 static int
510 tvhci_initchild(dev_info_t *dip, dev_info_t *child)
511 {
512 	_NOTE(ARGUNUSED(dip))
513 	char *guid, *addr;
514 
515 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
516 	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
517 		cmn_err(CE_NOTE, "tvhci_initchild - no guid property");
518 		return (DDI_FAILURE);
519 	}
520 
521 	addr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
522 	(void) snprintf(addr, MAXNAMELEN, "g%s", guid);
523 	ddi_set_name_addr(child, addr);
524 
525 	kmem_free(addr, MAXNAMELEN);
526 	ddi_prop_free(guid);
527 	return (DDI_SUCCESS);
528 }
529 
530 /*ARGSUSED*/
531 static int
532 tvhci_uninitchild(dev_info_t *dip, dev_info_t *child)
533 {
534 	ddi_set_name_addr(child, NULL);
535 	return (DDI_SUCCESS);
536 }
537 
538 /* form paddr by cname@<phci_inst>,<guid> */
539 static char *
540 tvh_get_phci_devname(char *cname, char *guid,
541     dev_info_t *pdip, char *pname, int len)
542 {
543 	(void) snprintf(pname, len, "%s@%d,%s",
544 	    cname, ddi_get_instance(pdip), guid);
545 	return (pname);
546 }
547 
548 static int
549 tvh_enum_by_phci(dev_info_t *vdip, char *devnm, int flags)
550 {
551 	mdi_phci_t *ph;
552 	mdi_vhci_t *vh = (mdi_vhci_t *)DEVI(vdip)->devi_mdi_xhci;
553 	dev_info_t *pdip, *cdip;
554 	char *cname, *caddr, *guid, *pname;
555 	int rval = DDI_FAILURE;
556 
557 	(void) i_ddi_parse_name(devnm, &cname, &caddr, NULL);
558 	if (cname == NULL || caddr == NULL || caddr[0] != 'g')
559 		return (rval);
560 
561 	guid = caddr + 1;
562 	pname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
563 
564 	/* mutex_enter(&mdi_mutex); XXX need lock access */
565 	ph = vh->vh_phci_head;
566 	while (ph) {
567 		pdip = ph->ph_dip;
568 		/* mutex_exit(&mdi_mutex); */
569 		(void) tvh_get_phci_devname(cname, guid, pdip, pname,
570 		    MAXNAMELEN);
571 		if (ndi_devi_config_one(pdip, pname, &cdip, flags)
572 		    == DDI_SUCCESS) {
573 			ndi_rele_devi(cdip);
574 			rval = DDI_SUCCESS;
575 		}
576 		/* mutex_enter(&mdi_mutex); */
577 		ph = ph->ph_next;
578 	}
579 	/* mutex_exit(&mdi_mutex); */
580 
581 	*(caddr - 1) = '@';	/* undo damage from i_ddi_parse_name() */
582 	kmem_free(pname, MAXNAMELEN);
583 	return (rval);
584 }
585 
586 static int
587 tvh_remove_by_phci(dev_info_t *vdip, char *devnm, int flags)
588 {
589 	int rval = DDI_SUCCESS;
590 	mdi_phci_t *ph;
591 	mdi_vhci_t *vh = (mdi_vhci_t *)DEVI(vdip)->devi_mdi_xhci;
592 	dev_info_t *pdip;
593 	char *cname, *caddr, *guid, *pname;
594 
595 	(void) i_ddi_parse_name(devnm, &cname, &caddr, NULL);
596 	if (cname == NULL || caddr == NULL || caddr[0] != 'g')
597 		return (rval);	/* devnm can't exist */
598 
599 	guid = caddr + 1;
600 	pname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
601 
602 	/* mutex_enter(&mdi_mutex); XXX need lock access */
603 	ph = vh->vh_phci_head;
604 	while (ph) {
605 		pdip = ph->ph_dip;
606 		/* mutex_exit(&mdi_mutex); */
607 		(void) tvh_get_phci_devname(cname, guid, pdip, pname,
608 		    MAXNAMELEN);
609 		rval = ndi_devi_unconfig_one(pdip, pname, NULL, flags);
610 		/* mutex_enter(&mdi_mutex); */
611 		if (rval != NDI_SUCCESS)
612 			break;
613 		ph = ph->ph_next;
614 	}
615 	/* mutex_exit(&mdi_mutex); */
616 
617 	kmem_free(pname, MAXNAMELEN);
618 	return (rval);
619 }
620 
621 static int
622 tvhci_bus_config(dev_info_t *parent, uint_t flags,
623     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
624 {
625 	char *devnm;
626 	dev_info_t *cdip;
627 	int circ;
628 
629 	switch (op) {
630 	case BUS_CONFIG_ONE:
631 		break;
632 	case BUS_CONFIG_ALL:
633 		/* XXX call into phci's here? */
634 	case BUS_CONFIG_DRIVER:
635 		return (ndi_busop_bus_config(parent, flags, op, arg, childp,
636 		    0));
637 	default:
638 		return (DDI_FAILURE);
639 	}
640 
641 	devnm = (char *)arg;
642 	ndi_devi_enter(parent, &circ);
643 	cdip = ndi_devi_findchild(parent, devnm);
644 	ndi_devi_exit(parent, circ);
645 	if (cdip == NULL) {
646 		/* call into registered phci's */
647 		(void) tvh_enum_by_phci(parent, devnm, flags);
648 	}
649 
650 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
651 }
652 
653 static int
654 tvhci_bus_unconfig(dev_info_t *parent, uint_t flags,
655     ddi_bus_config_op_t op, void *arg)
656 {
657 	char *devnm, *slashname;
658 	int rval, circ;
659 	dev_info_t *cdip, *ndip;
660 
661 	/*
662 	 * If we are not removing device nodes, pathinfo can be
663 	 * left as is. So no need to disturb the phci's.
664 	 */
665 	if ((flags & NDI_DEVI_REMOVE) == 0) {
666 		return (ndi_busop_bus_unconfig(parent, flags, op, arg));
667 	}
668 
669 	switch (op) {
670 	case BUS_UNCONFIG_ONE:
671 		devnm = (char *)arg;
672 		ndi_devi_enter(parent, &circ);
673 		if (ndi_devi_findchild(parent, devnm) == NULL) {
674 			ndi_devi_exit(parent, circ);
675 			return (DDI_SUCCESS);
676 		}
677 		ndi_devi_exit(parent, circ);
678 		return (tvh_remove_by_phci(parent, devnm, flags));
679 
680 	case BUS_UNCONFIG_ALL:
681 		/* this functionality is for developers only */
682 		rval = DDI_SUCCESS;
683 		slashname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
684 		devnm = slashname + 1;
685 		ndi_devi_enter(parent, &circ);
686 		cdip = ddi_get_child(parent);
687 		while (cdip != NULL) {
688 			ndip = ddi_get_next_sibling(cdip);
689 			(void) ddi_deviname(cdip, slashname);
690 			ndi_devi_exit(parent, circ);
691 			rval = tvh_remove_by_phci(parent, devnm, flags);
692 			if (rval != DDI_SUCCESS) {
693 				break;
694 			}
695 			ndi_devi_enter(parent, &circ);
696 			cdip = ndip;
697 		}
698 		ndi_devi_exit(parent, circ);
699 		return (rval);
700 
701 	case BUS_UNCONFIG_DRIVER:
702 		/* unconfig driver never comes with NDI_DEVI_REMOVE */
703 	default:
704 		return (DDI_FAILURE);
705 	}
706 	/*NOTREACHED*/
707 }
708