xref: /illumos-gate/usr/src/uts/sun4v/io/cnex.c (revision 0de66c94)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * Logical domain channel devices are devices implemented entirely
29  * in software; cnex is the nexus for channel-devices. They use
30  * the HV channel interfaces via the LDC transport module to send
31  * and receive data and to register callbacks.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/cmn_err.h>
36 #include <sys/conf.h>
37 #include <sys/ddi.h>
38 #include <sys/ddi_impldefs.h>
39 #include <sys/devops.h>
40 #include <sys/instance.h>
41 #include <sys/modctl.h>
42 #include <sys/open.h>
43 #include <sys/stat.h>
44 #include <sys/sunddi.h>
45 #include <sys/sunndi.h>
46 #include <sys/systm.h>
47 #include <sys/mkdev.h>
48 #include <sys/machsystm.h>
49 #include <sys/intreg.h>
50 #include <sys/intr.h>
51 #include <sys/ddi_intr_impl.h>
52 #include <sys/ivintr.h>
53 #include <sys/hypervisor_api.h>
54 #include <sys/ldc.h>
55 #include <sys/cnex.h>
56 #include <sys/mach_descrip.h>
57 #include <sys/hsvc.h>
58 #include <sys/sdt.h>
59 
60 /*
61  * Internal functions/information
62  */
63 static struct cnex_intr_map cnex_class_to_intr[] = {
64 	{LDC_DEV_GENERIC,	PIL_3,	 0},
65 	{LDC_DEV_BLK,		PIL_4,	10},
66 	{LDC_DEV_BLK_SVC,	PIL_3,	10},
67 	{LDC_DEV_NT,		PIL_6,	35},
68 	{LDC_DEV_NT_SVC,	PIL_4,	35},
69 	{LDC_DEV_SERIAL,	PIL_6,	 0}
70 };
71 #define	CNEX_MAX_DEVS (sizeof (cnex_class_to_intr) / \
72 				sizeof (cnex_class_to_intr[0]))
73 
74 #define	CNEX_TX_INTR_WEIGHT	0
75 
76 #define	SUN4V_REG_SPEC2CFG_HDL(x)	((x >> 32) & ~(0xfull << 28))
77 
78 static clock_t cnex_wait_usecs = 1000; /* wait time in usecs */
79 static int cnex_wait_retries = 3;
80 static void *cnex_state;
81 
82 static uint_t cnex_intr_wrapper(caddr_t arg);
83 static dev_info_t *cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id,
84     md_t *mdp, mde_cookie_t mde);
85 
86 /*
87  * Channel Interrupt Distribution
88  *
89  * In order to balance interrupts among available CPUs, we use
90  * the intr_dist_cpuid_{add,remove}_device_weight() interface to
91  * assign weights to channel interrupts. These weights, which are
92  * defined in the cnex_intr_map structure, influence which CPU
93  * is returned by intr_dist_cpuid() when called via the cnex
94  * interrupt redistribution callback cnex_intr_redist().
95  * Interrupts for VIO devclass channels are given more weight than
96  * other interrupts because they are expected to occur more
97  * frequently and have a larger impact on overall performance.
98  * Transmit interrupts are given a zero weight because they are
99  * not used.
100  *
101  * The interrupt weights influence the target CPU selection when
102  * interrupts are redistributed and when they are added. However,
103  * removal of interrupts can unbalance the distribution even if
104  * they are removed in converse order--compared to the order they
105  * are added. This can occur when interrupts are removed after
106  * redistribution occurs.
107  *
108  * Channel interrupt weights affect interrupt-CPU distribution
109  * relative to other weighted interrupts on the system. For VIO
110  * devclass channels, values are chosen to match those used by
111  * the PCI express nexus driver for net and storage devices.
112  */
113 static void cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight);
114 static int cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo);
115 static int cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo);
116 static int32_t cnex_class_weight(ldc_dev_t devclass);
117 
118 /*
119  * Debug info
120  */
121 #ifdef DEBUG
122 
123 /*
124  * Print debug messages
125  *
126  * set cnexdbg to 0xf for enabling all msgs
127  * 0x8 - Errors
128  * 0x4 - Warnings
129  * 0x2 - All debug messages
130  * 0x1 - Minimal debug messages
131  */
132 
133 int cnexdbg = 0x8;
134 
135 static void
136 cnexdebug(const char *fmt, ...)
137 {
138 	char buf[512];
139 	va_list ap;
140 
141 	va_start(ap, fmt);
142 	(void) vsprintf(buf, fmt, ap);
143 	va_end(ap);
144 
145 	cmn_err(CE_CONT, "%s\n", buf);
146 }
147 
148 #define	D1		\
149 if (cnexdbg & 0x01)	\
150 	cnexdebug
151 
152 #define	D2		\
153 if (cnexdbg & 0x02)	\
154 	cnexdebug
155 
156 #define	DWARN		\
157 if (cnexdbg & 0x04)	\
158 	cnexdebug
159 
160 #define	DERR		\
161 if (cnexdbg & 0x08)	\
162 	cnexdebug
163 
164 #else
165 
166 #define	D1
167 #define	D2
168 #define	DWARN
169 #define	DERR
170 
171 #endif
172 
173 /*
174  * Config information
175  */
176 static int cnex_attach(dev_info_t *, ddi_attach_cmd_t);
177 static int cnex_detach(dev_info_t *, ddi_detach_cmd_t);
178 static int cnex_open(dev_t *, int, int, cred_t *);
179 static int cnex_close(dev_t, int, int, cred_t *);
180 static int cnex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
181 static int cnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
182     void *);
183 
184 static struct bus_ops cnex_bus_ops = {
185 	BUSO_REV,
186 	nullbusmap,		/* bus_map */
187 	NULL,			/* bus_get_intrspec */
188 	NULL,			/* bus_add_intrspec */
189 	NULL,			/* bus_remove_intrspec */
190 	i_ddi_map_fault,	/* bus_map_fault */
191 	ddi_no_dma_map,		/* bus_dma_map */
192 	ddi_no_dma_allochdl,	/* bus_dma_allochdl */
193 	NULL,			/* bus_dma_freehdl */
194 	NULL,			/* bus_dma_bindhdl */
195 	NULL,			/* bus_dma_unbindhdl */
196 	NULL,			/* bus_dma_flush */
197 	NULL,			/* bus_dma_win */
198 	NULL,			/* bus_dma_ctl */
199 	cnex_ctl,		/* bus_ctl */
200 	ddi_bus_prop_op,	/* bus_prop_op */
201 	0,			/* bus_get_eventcookie */
202 	0,			/* bus_add_eventcall */
203 	0,			/* bus_remove_eventcall	*/
204 	0,			/* bus_post_event */
205 	NULL,			/* bus_intr_ctl */
206 	NULL,			/* bus_config */
207 	NULL,			/* bus_unconfig */
208 	NULL,			/* bus_fm_init */
209 	NULL,			/* bus_fm_fini */
210 	NULL,			/* bus_fm_access_enter */
211 	NULL,			/* bus_fm_access_exit */
212 	NULL,			/* bus_power */
213 	NULL			/* bus_intr_op */
214 };
215 
216 static struct cb_ops cnex_cb_ops = {
217 	cnex_open,			/* open */
218 	cnex_close,			/* close */
219 	nodev,				/* strategy */
220 	nodev,				/* print */
221 	nodev,				/* dump */
222 	nodev,				/* read */
223 	nodev,				/* write */
224 	cnex_ioctl,			/* ioctl */
225 	nodev,				/* devmap */
226 	nodev,				/* mmap */
227 	nodev,				/* segmap */
228 	nochpoll,			/* poll */
229 	ddi_prop_op,			/* cb_prop_op */
230 	0,				/* streamtab  */
231 	D_MP | D_NEW | D_HOTPLUG	/* Driver compatibility flag */
232 };
233 
234 static struct dev_ops cnex_ops = {
235 	DEVO_REV,		/* devo_rev, */
236 	0,			/* refcnt  */
237 	ddi_getinfo_1to1,	/* info */
238 	nulldev,		/* identify */
239 	nulldev,		/* probe */
240 	cnex_attach,		/* attach */
241 	cnex_detach,		/* detach */
242 	nodev,			/* reset */
243 	&cnex_cb_ops,		/* driver operations */
244 	&cnex_bus_ops,		/* bus operations */
245 	nulldev,		/* power */
246 	ddi_quiesce_not_needed,		/* quiesce */
247 };
248 
249 /*
250  * Module linkage information for the kernel.
251  */
252 static struct modldrv modldrv = {
253 	&mod_driverops,
254 	"sun4v channel-devices nexus",
255 	&cnex_ops,
256 };
257 
258 static struct modlinkage modlinkage = {
259 	MODREV_1, (void *)&modldrv, NULL
260 };
261 
262 int
263 _init(void)
264 {
265 	int err;
266 	uint64_t majornum;
267 	uint64_t minornum;
268 
269 	/*
270 	 * Check HV intr group api versioning.
271 	 * Note that cnex assumes interrupt cookies is
272 	 * in version 1.0 of the intr group api.
273 	 */
274 	if ((err = hsvc_version(HSVC_GROUP_INTR, &majornum, &minornum)) != 0) {
275 		cmn_err(CE_WARN, "cnex: failed to get intr api "
276 		    "group versioning errno=%d", err);
277 		return (err);
278 	} else if ((majornum != 1) && (majornum != 2)) {
279 		cmn_err(CE_WARN, "cnex: unsupported intr api group: "
280 		    "maj:0x%lx, min:0x%lx", majornum, minornum);
281 		return (ENOTSUP);
282 	}
283 
284 	if ((err = ddi_soft_state_init(&cnex_state,
285 	    sizeof (cnex_soft_state_t), 0)) != 0) {
286 		return (err);
287 	}
288 	if ((err = mod_install(&modlinkage)) != 0) {
289 		ddi_soft_state_fini(&cnex_state);
290 		return (err);
291 	}
292 	return (0);
293 }
294 
295 int
296 _fini(void)
297 {
298 	int err;
299 
300 	if ((err = mod_remove(&modlinkage)) != 0)
301 		return (err);
302 	ddi_soft_state_fini(&cnex_state);
303 	return (0);
304 }
305 
306 int
307 _info(struct modinfo *modinfop)
308 {
309 	return (mod_info(&modlinkage, modinfop));
310 }
311 
312 /*
313  * Callback function invoked by the interrupt redistribution
314  * framework. This will redirect interrupts at CPUs that are
315  * currently available in the system.
316  *
317  * Note: any interrupts with weight greater than or equal to
318  * weight_max must be redistributed when this callback is
319  * invoked with (weight == weight_max) which will be once per
320  * redistribution.
321  */
322 /*ARGSUSED*/
323 static void
324 cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight)
325 {
326 	cnex_ldc_t		*cldcp;
327 	cnex_soft_state_t	*cnex_ssp = arg;
328 
329 	ASSERT(cnex_ssp != NULL);
330 	mutex_enter(&cnex_ssp->clist_lock);
331 
332 	cldcp = cnex_ssp->clist;
333 	while (cldcp != NULL) {
334 
335 		mutex_enter(&cldcp->lock);
336 
337 		if (cldcp->tx.hdlr && (cldcp->tx.weight == weight ||
338 		    (weight_max == weight && cldcp->tx.weight > weight))) {
339 			(void) cnex_intr_new_cpu(cnex_ssp, &cldcp->tx);
340 		}
341 
342 		if (cldcp->rx.hdlr && (cldcp->rx.weight == weight ||
343 		    (weight_max == weight && cldcp->rx.weight > weight))) {
344 			(void) cnex_intr_new_cpu(cnex_ssp, &cldcp->rx);
345 		}
346 
347 		mutex_exit(&cldcp->lock);
348 
349 		/* next channel */
350 		cldcp = cldcp->next;
351 	}
352 
353 	mutex_exit(&cnex_ssp->clist_lock);
354 }
355 
356 /*
357  * Internal function to replace the CPU used by an interrupt
358  * during interrupt redistribution.
359  */
360 static int
361 cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo)
362 {
363 	int	intr_state;
364 	int 	rv;
365 
366 	/* Determine if the interrupt is enabled */
367 	rv = hvldc_intr_getvalid(ssp->cfghdl, iinfo->ino, &intr_state);
368 	if (rv) {
369 		DWARN("cnex_intr_new_cpu: rx ino=0x%llx, can't get valid\n",
370 		    iinfo->ino);
371 		return (rv);
372 	}
373 
374 	/* If it is enabled, disable it */
375 	if (intr_state == HV_INTR_VALID) {
376 		rv = cnex_intr_dis_wait(ssp, iinfo);
377 		if (rv) {
378 			return (rv);
379 		}
380 	}
381 
382 	/* Target the interrupt at a new CPU. */
383 	iinfo->cpuid = intr_dist_cpuid();
384 	(void) hvldc_intr_settarget(ssp->cfghdl, iinfo->ino, iinfo->cpuid);
385 	intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip,
386 	    iinfo->weight);
387 
388 	/* Re-enable the interrupt if it was enabled */
389 	if (intr_state == HV_INTR_VALID) {
390 		(void) hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino,
391 		    HV_INTR_VALID);
392 	}
393 
394 	return (0);
395 }
396 
397 /*
398  * Internal function to disable an interrupt and wait
399  * for any pending interrupts to finish.
400  */
401 static int
402 cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo)
403 {
404 	int rv, intr_state, retries;
405 
406 	/* disable interrupts */
407 	rv = hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino, HV_INTR_NOTVALID);
408 	if (rv) {
409 		DWARN("cnex_intr_dis_wait: ino=0x%llx, can't set valid\n",
410 		    iinfo->ino);
411 		return (ENXIO);
412 	}
413 
414 	/*
415 	 * Make a best effort to wait for pending interrupts
416 	 * to finish. There is not much we can do if we timeout.
417 	 */
418 	retries = 0;
419 
420 	do {
421 		rv = hvldc_intr_getstate(ssp->cfghdl, iinfo->ino, &intr_state);
422 		if (rv) {
423 			DWARN("cnex_intr_dis_wait: ino=0x%llx, can't get "
424 			    "state\n", iinfo->ino);
425 			return (ENXIO);
426 		}
427 
428 		if (intr_state != HV_INTR_DELIVERED_STATE)
429 			break;
430 
431 		drv_usecwait(cnex_wait_usecs);
432 
433 	} while (!panicstr && ++retries <= cnex_wait_retries);
434 
435 	return (0);
436 }
437 
438 /*
439  * Returns the interrupt weight to use for the specified devclass.
440  */
441 static int32_t
442 cnex_class_weight(ldc_dev_t devclass)
443 {
444 	int idx;
445 
446 	for (idx = 0; idx < CNEX_MAX_DEVS; idx++) {
447 		if (devclass == cnex_class_to_intr[idx].devclass) {
448 			return (cnex_class_to_intr[idx].weight);
449 		}
450 	}
451 
452 	/*
453 	 * If this code is reached, the specified devclass is
454 	 * invalid. New devclasses should be added to
455 	 * cnex_class_to_intr.
456 	 */
457 	ASSERT(0);
458 
459 	return (0);
460 }
461 
462 /*
463  * Exported interface to register a LDC endpoint with
464  * the channel nexus
465  */
466 static int
467 cnex_reg_chan(dev_info_t *dip, uint64_t id, ldc_dev_t devclass)
468 {
469 	int		idx;
470 	cnex_ldc_t	*cldcp;
471 	cnex_ldc_t	*new_cldcp;
472 	int		listsz, num_nodes, num_channels;
473 	md_t		*mdp = NULL;
474 	mde_cookie_t	rootnode, *listp = NULL;
475 	uint64_t	tmp_id;
476 	uint64_t	rxino = (uint64_t)-1;
477 	uint64_t	txino = (uint64_t)-1;
478 	cnex_soft_state_t *cnex_ssp;
479 	int		status, instance;
480 	dev_info_t	*chan_dip = NULL;
481 
482 	/* Get device instance and structure */
483 	instance = ddi_get_instance(dip);
484 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
485 
486 	/* Check to see if channel is already registered */
487 	mutex_enter(&cnex_ssp->clist_lock);
488 	cldcp = cnex_ssp->clist;
489 	while (cldcp) {
490 		if (cldcp->id == id) {
491 			DWARN("cnex_reg_chan: channel 0x%llx exists\n", id);
492 			mutex_exit(&cnex_ssp->clist_lock);
493 			return (EINVAL);
494 		}
495 		cldcp = cldcp->next;
496 	}
497 	mutex_exit(&cnex_ssp->clist_lock);
498 
499 	/* Get the Tx/Rx inos from the MD */
500 	if ((mdp = md_get_handle()) == NULL) {
501 		DWARN("cnex_reg_chan: cannot init MD\n");
502 		return (ENXIO);
503 	}
504 	num_nodes = md_node_count(mdp);
505 	ASSERT(num_nodes > 0);
506 
507 	listsz = num_nodes * sizeof (mde_cookie_t);
508 	listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP);
509 
510 	rootnode = md_root_node(mdp);
511 
512 	/* search for all channel_endpoint nodes */
513 	num_channels = md_scan_dag(mdp, rootnode,
514 	    md_find_name(mdp, "channel-endpoint"),
515 	    md_find_name(mdp, "fwd"), listp);
516 	if (num_channels <= 0) {
517 		DWARN("cnex_reg_chan: invalid channel id\n");
518 		kmem_free(listp, listsz);
519 		(void) md_fini_handle(mdp);
520 		return (EINVAL);
521 	}
522 
523 	for (idx = 0; idx < num_channels; idx++) {
524 
525 		/* Get the channel ID */
526 		status = md_get_prop_val(mdp, listp[idx], "id", &tmp_id);
527 		if (status) {
528 			DWARN("cnex_reg_chan: cannot read LDC ID\n");
529 			kmem_free(listp, listsz);
530 			(void) md_fini_handle(mdp);
531 			return (ENXIO);
532 		}
533 		if (tmp_id != id)
534 			continue;
535 
536 		/* Get the Tx and Rx ino */
537 		status = md_get_prop_val(mdp, listp[idx], "tx-ino", &txino);
538 		if (status) {
539 			DWARN("cnex_reg_chan: cannot read Tx ino\n");
540 			kmem_free(listp, listsz);
541 			(void) md_fini_handle(mdp);
542 			return (ENXIO);
543 		}
544 		status = md_get_prop_val(mdp, listp[idx], "rx-ino", &rxino);
545 		if (status) {
546 			DWARN("cnex_reg_chan: cannot read Rx ino\n");
547 			kmem_free(listp, listsz);
548 			(void) md_fini_handle(mdp);
549 			return (ENXIO);
550 		}
551 		chan_dip = cnex_find_chan_dip(dip, id, mdp, listp[idx]);
552 		ASSERT(chan_dip != NULL);
553 	}
554 	kmem_free(listp, listsz);
555 	(void) md_fini_handle(mdp);
556 
557 	/*
558 	 * check to see if we looped through the list of channel IDs without
559 	 * matching one (i.e. an 'ino' has not been initialised).
560 	 */
561 	if ((rxino == -1) || (txino == -1)) {
562 		DERR("cnex_reg_chan: no ID matching '%llx' in MD\n", id);
563 		return (ENOENT);
564 	}
565 
566 	/* Allocate a new channel structure */
567 	new_cldcp = kmem_zalloc(sizeof (*new_cldcp), KM_SLEEP);
568 
569 	/* Initialize the channel */
570 	mutex_init(&new_cldcp->lock, NULL, MUTEX_DRIVER, NULL);
571 
572 	new_cldcp->id = id;
573 	new_cldcp->tx.ino = txino;
574 	new_cldcp->rx.ino = rxino;
575 	new_cldcp->devclass = devclass;
576 	new_cldcp->tx.weight = CNEX_TX_INTR_WEIGHT;
577 	new_cldcp->rx.weight = cnex_class_weight(devclass);
578 	new_cldcp->dip = chan_dip;
579 
580 	/*
581 	 * Add channel to nexus channel list.
582 	 * Check again to see if channel is already registered since
583 	 * clist_lock was dropped above.
584 	 */
585 	mutex_enter(&cnex_ssp->clist_lock);
586 	cldcp = cnex_ssp->clist;
587 	while (cldcp) {
588 		if (cldcp->id == id) {
589 			DWARN("cnex_reg_chan: channel 0x%llx exists\n", id);
590 			mutex_exit(&cnex_ssp->clist_lock);
591 			mutex_destroy(&new_cldcp->lock);
592 			kmem_free(new_cldcp, sizeof (*new_cldcp));
593 			return (EINVAL);
594 		}
595 		cldcp = cldcp->next;
596 	}
597 	new_cldcp->next = cnex_ssp->clist;
598 	cnex_ssp->clist = new_cldcp;
599 	mutex_exit(&cnex_ssp->clist_lock);
600 
601 	return (0);
602 }
603 
604 /*
605  * Add Tx/Rx interrupt handler for the channel
606  */
607 static int
608 cnex_add_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype,
609     uint_t (*hdlr)(), caddr_t arg1, caddr_t arg2)
610 {
611 	int		rv, idx, pil;
612 	cnex_ldc_t	*cldcp;
613 	cnex_intr_t	*iinfo;
614 	cnex_soft_state_t *cnex_ssp;
615 	int		instance;
616 
617 	/* Get device instance and structure */
618 	instance = ddi_get_instance(dip);
619 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
620 
621 	/* get channel info */
622 	mutex_enter(&cnex_ssp->clist_lock);
623 	cldcp = cnex_ssp->clist;
624 	while (cldcp) {
625 		if (cldcp->id == id)
626 			break;
627 		cldcp = cldcp->next;
628 	}
629 	if (cldcp == NULL) {
630 		DWARN("cnex_add_intr: channel 0x%llx does not exist\n", id);
631 		mutex_exit(&cnex_ssp->clist_lock);
632 		return (EINVAL);
633 	}
634 	mutex_exit(&cnex_ssp->clist_lock);
635 
636 	/* get channel lock */
637 	mutex_enter(&cldcp->lock);
638 
639 	/* get interrupt type */
640 	if (itype == CNEX_TX_INTR) {
641 		iinfo = &(cldcp->tx);
642 	} else if (itype == CNEX_RX_INTR) {
643 		iinfo = &(cldcp->rx);
644 	} else {
645 		DWARN("cnex_add_intr: invalid interrupt type\n", id);
646 		mutex_exit(&cldcp->lock);
647 		return (EINVAL);
648 	}
649 
650 	/* check if a handler is already added */
651 	if (iinfo->hdlr != 0) {
652 		DWARN("cnex_add_intr: interrupt handler exists\n");
653 		mutex_exit(&cldcp->lock);
654 		return (EINVAL);
655 	}
656 
657 	/* save interrupt handler info */
658 	iinfo->hdlr = hdlr;
659 	iinfo->arg1 = arg1;
660 	iinfo->arg2 = arg2;
661 
662 	/* save data for DTrace probes used by intrstat(1m) */
663 	iinfo->dip = cldcp->dip;
664 	iinfo->id = cldcp->id;
665 
666 	iinfo->icookie = MINVINTR_COOKIE + iinfo->ino;
667 
668 	/*
669 	 * Verify that the ino does not generate a cookie which
670 	 * is outside the (MINVINTR_COOKIE, MAXIVNUM) range of the
671 	 * system interrupt table.
672 	 */
673 	if (iinfo->icookie >= MAXIVNUM || iinfo->icookie < MINVINTR_COOKIE) {
674 		DWARN("cnex_add_intr: invalid cookie %x ino %x\n",
675 		    iinfo->icookie, iinfo->ino);
676 		mutex_exit(&cldcp->lock);
677 		return (EINVAL);
678 	}
679 
680 	D1("cnex_add_intr: add hdlr, cfghdl=0x%llx, ino=0x%llx, "
681 	    "cookie=0x%llx\n", cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie);
682 
683 	/* Pick a PIL on the basis of the channel's devclass */
684 	for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) {
685 		if (cldcp->devclass == cnex_class_to_intr[idx].devclass) {
686 			pil = cnex_class_to_intr[idx].pil;
687 			break;
688 		}
689 	}
690 
691 	/* add interrupt to solaris ivec table */
692 	if (add_ivintr(iinfo->icookie, pil, (intrfunc)cnex_intr_wrapper,
693 	    (caddr_t)iinfo, NULL, NULL) != 0) {
694 		DWARN("cnex_add_intr: add_ivintr fail cookie %x ino %x\n",
695 		    iinfo->icookie, iinfo->ino);
696 		mutex_exit(&cldcp->lock);
697 		return (EINVAL);
698 	}
699 
700 	/* set the cookie in the HV */
701 	rv = hvldc_intr_setcookie(cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie);
702 
703 	/* pick next CPU in the domain for this channel */
704 	iinfo->cpuid = intr_dist_cpuid();
705 
706 	/* set the target CPU and then enable interrupts */
707 	rv = hvldc_intr_settarget(cnex_ssp->cfghdl, iinfo->ino, iinfo->cpuid);
708 	if (rv) {
709 		DWARN("cnex_add_intr: ino=0x%llx, cannot set target cpu\n",
710 		    iinfo->ino);
711 		goto hv_error;
712 	}
713 	rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino,
714 	    HV_INTR_IDLE_STATE);
715 	if (rv) {
716 		DWARN("cnex_add_intr: ino=0x%llx, cannot set state\n",
717 		    iinfo->ino);
718 		goto hv_error;
719 	}
720 	rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, iinfo->ino, HV_INTR_VALID);
721 	if (rv) {
722 		DWARN("cnex_add_intr: ino=0x%llx, cannot set valid\n",
723 		    iinfo->ino);
724 		goto hv_error;
725 	}
726 
727 	intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip,
728 	    iinfo->weight);
729 
730 	mutex_exit(&cldcp->lock);
731 	return (0);
732 
733 hv_error:
734 	(void) rem_ivintr(iinfo->icookie, pil);
735 	mutex_exit(&cldcp->lock);
736 	return (ENXIO);
737 }
738 
739 
740 /*
741  * Exported interface to unregister a LDC endpoint with
742  * the channel nexus
743  */
744 static int
745 cnex_unreg_chan(dev_info_t *dip, uint64_t id)
746 {
747 	cnex_ldc_t	*cldcp, *prev_cldcp;
748 	cnex_soft_state_t *cnex_ssp;
749 	int		instance;
750 
751 	/* Get device instance and structure */
752 	instance = ddi_get_instance(dip);
753 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
754 
755 	/* find and remove channel from list */
756 	mutex_enter(&cnex_ssp->clist_lock);
757 	prev_cldcp = NULL;
758 	cldcp = cnex_ssp->clist;
759 	while (cldcp) {
760 		if (cldcp->id == id)
761 			break;
762 		prev_cldcp = cldcp;
763 		cldcp = cldcp->next;
764 	}
765 
766 	if (cldcp == 0) {
767 		DWARN("cnex_unreg_chan: invalid channel %d\n", id);
768 		mutex_exit(&cnex_ssp->clist_lock);
769 		return (EINVAL);
770 	}
771 
772 	if (cldcp->tx.hdlr || cldcp->rx.hdlr) {
773 		DWARN("cnex_unreg_chan: handlers still exist: chan %lx\n", id);
774 		mutex_exit(&cnex_ssp->clist_lock);
775 		return (ENXIO);
776 	}
777 
778 	if (prev_cldcp)
779 		prev_cldcp->next = cldcp->next;
780 	else
781 		cnex_ssp->clist = cldcp->next;
782 
783 	mutex_exit(&cnex_ssp->clist_lock);
784 
785 	/* destroy mutex */
786 	mutex_destroy(&cldcp->lock);
787 
788 	/* free channel */
789 	kmem_free(cldcp, sizeof (*cldcp));
790 
791 	return (0);
792 }
793 
794 /*
795  * Remove Tx/Rx interrupt handler for the channel
796  */
797 static int
798 cnex_rem_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype)
799 {
800 	int			rv, idx, pil;
801 	cnex_ldc_t		*cldcp;
802 	cnex_intr_t		*iinfo;
803 	cnex_soft_state_t	*cnex_ssp;
804 	int			instance, istate;
805 
806 	/* Get device instance and structure */
807 	instance = ddi_get_instance(dip);
808 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
809 
810 	/* get channel info */
811 	mutex_enter(&cnex_ssp->clist_lock);
812 	cldcp = cnex_ssp->clist;
813 	while (cldcp) {
814 		if (cldcp->id == id)
815 			break;
816 		cldcp = cldcp->next;
817 	}
818 	if (cldcp == NULL) {
819 		DWARN("cnex_rem_intr: channel 0x%llx does not exist\n", id);
820 		mutex_exit(&cnex_ssp->clist_lock);
821 		return (EINVAL);
822 	}
823 	mutex_exit(&cnex_ssp->clist_lock);
824 
825 	/* get rid of the channel intr handler */
826 	mutex_enter(&cldcp->lock);
827 
828 	/* get interrupt type */
829 	if (itype == CNEX_TX_INTR) {
830 		iinfo = &(cldcp->tx);
831 	} else if (itype == CNEX_RX_INTR) {
832 		iinfo = &(cldcp->rx);
833 	} else {
834 		DWARN("cnex_rem_intr: invalid interrupt type\n");
835 		mutex_exit(&cldcp->lock);
836 		return (EINVAL);
837 	}
838 
839 	D1("cnex_rem_intr: interrupt ino=0x%x\n", iinfo->ino);
840 
841 	/* check if a handler is already added */
842 	if (iinfo->hdlr == 0) {
843 		DWARN("cnex_rem_intr: interrupt handler does not exist\n");
844 		mutex_exit(&cldcp->lock);
845 		return (EINVAL);
846 	}
847 
848 	D1("cnex_rem_intr: set intr to invalid ino=0x%x\n", iinfo->ino);
849 	rv = hvldc_intr_setvalid(cnex_ssp->cfghdl,
850 	    iinfo->ino, HV_INTR_NOTVALID);
851 	if (rv) {
852 		DWARN("cnex_rem_intr: cannot set valid ino=%x\n", iinfo->ino);
853 		mutex_exit(&cldcp->lock);
854 		return (ENXIO);
855 	}
856 
857 	/*
858 	 * Check if there are pending interrupts. If interrupts are
859 	 * pending return EAGAIN.
860 	 */
861 	rv = hvldc_intr_getstate(cnex_ssp->cfghdl, iinfo->ino, &istate);
862 	if (rv) {
863 		DWARN("cnex_rem_intr: ino=0x%llx, cannot get state\n",
864 		    iinfo->ino);
865 		mutex_exit(&cldcp->lock);
866 		return (ENXIO);
867 	}
868 
869 	/* if interrupts are still pending print warning */
870 	if (istate != HV_INTR_IDLE_STATE) {
871 		DWARN("cnex_rem_intr: cannot remove intr busy ino=%x\n",
872 		    iinfo->ino);
873 		mutex_exit(&cldcp->lock);
874 		return (EAGAIN);
875 	}
876 
877 	/* Pick a PIL on the basis of the channel's devclass */
878 	for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) {
879 		if (cldcp->devclass == cnex_class_to_intr[idx].devclass) {
880 			pil = cnex_class_to_intr[idx].pil;
881 			break;
882 		}
883 	}
884 
885 	intr_dist_cpuid_rem_device_weight(iinfo->cpuid, iinfo->dip);
886 
887 	/* remove interrupt */
888 	(void) rem_ivintr(iinfo->icookie, pil);
889 
890 	/* clear interrupt info */
891 	bzero(iinfo, sizeof (*iinfo));
892 
893 	mutex_exit(&cldcp->lock);
894 
895 	return (0);
896 }
897 
898 
899 /*
900  * Clear pending Tx/Rx interrupt
901  */
902 static int
903 cnex_clr_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype)
904 {
905 	int			rv;
906 	cnex_ldc_t		*cldcp;
907 	cnex_intr_t		*iinfo;
908 	cnex_soft_state_t	*cnex_ssp;
909 	int			instance;
910 
911 	/* Get device instance and structure */
912 	instance = ddi_get_instance(dip);
913 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
914 
915 	/* get channel info */
916 	mutex_enter(&cnex_ssp->clist_lock);
917 	cldcp = cnex_ssp->clist;
918 	while (cldcp) {
919 		if (cldcp->id == id)
920 			break;
921 		cldcp = cldcp->next;
922 	}
923 	if (cldcp == NULL) {
924 		DWARN("cnex_clr_intr: channel 0x%llx does not exist\n", id);
925 		mutex_exit(&cnex_ssp->clist_lock);
926 		return (EINVAL);
927 	}
928 	mutex_exit(&cnex_ssp->clist_lock);
929 
930 	mutex_enter(&cldcp->lock);
931 
932 	/* get interrupt type */
933 	if (itype == CNEX_TX_INTR) {
934 		iinfo = &(cldcp->tx);
935 	} else if (itype == CNEX_RX_INTR) {
936 		iinfo = &(cldcp->rx);
937 	} else {
938 		DWARN("cnex_clr_intr: invalid interrupt type\n");
939 		mutex_exit(&cldcp->lock);
940 		return (EINVAL);
941 	}
942 
943 	D1("%s: interrupt ino=0x%x\n", __func__, iinfo->ino);
944 
945 	/* check if a handler is already added */
946 	if (iinfo->hdlr == 0) {
947 		DWARN("cnex_clr_intr: interrupt handler does not exist\n");
948 		mutex_exit(&cldcp->lock);
949 		return (EINVAL);
950 	}
951 
952 	rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino,
953 	    HV_INTR_IDLE_STATE);
954 	if (rv) {
955 		DWARN("cnex_clr_intr: cannot clear interrupt state\n");
956 		mutex_exit(&cldcp->lock);
957 		return (ENXIO);
958 	}
959 
960 	mutex_exit(&cldcp->lock);
961 
962 	return (0);
963 }
964 
965 /*
966  * Channel nexus interrupt handler wrapper
967  */
968 static uint_t
969 cnex_intr_wrapper(caddr_t arg)
970 {
971 	int 			res;
972 	uint_t 			(*handler)();
973 	caddr_t 		handler_arg1;
974 	caddr_t 		handler_arg2;
975 	cnex_intr_t 		*iinfo = (cnex_intr_t *)arg;
976 
977 	ASSERT(iinfo != NULL);
978 
979 	handler = iinfo->hdlr;
980 	handler_arg1 = iinfo->arg1;
981 	handler_arg2 = iinfo->arg2;
982 
983 	/*
984 	 * The 'interrupt__start' and 'interrupt__complete' probes
985 	 * are provided to support 'intrstat' command. These probes
986 	 * help monitor the interrupts on a per device basis only.
987 	 * In order to provide the ability to monitor the
988 	 * activity on a per channel basis, two additional
989 	 * probes('channelintr__start','channelintr__complete')
990 	 * are provided here.
991 	 */
992 	DTRACE_PROBE4(channelintr__start, uint64_t, iinfo->id,
993 	    cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1);
994 
995 	DTRACE_PROBE4(interrupt__start, dev_info_t, iinfo->dip,
996 	    void *, handler, caddr_t, handler_arg1, caddr_t, handler_arg2);
997 
998 	D1("cnex_intr_wrapper:ino=0x%llx invoke client handler\n", iinfo->ino);
999 	res = (*handler)(handler_arg1, handler_arg2);
1000 
1001 	DTRACE_PROBE4(interrupt__complete, dev_info_t, iinfo->dip,
1002 	    void *, handler, caddr_t, handler_arg1, int, res);
1003 
1004 	DTRACE_PROBE4(channelintr__complete, uint64_t, iinfo->id,
1005 	    cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1);
1006 
1007 	return (res);
1008 }
1009 
1010 /*ARGSUSED*/
1011 static int
1012 cnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
1013 {
1014 	int 		rv, instance, reglen;
1015 	cnex_regspec_t	*reg_p;
1016 	ldc_cnex_t	cinfo;
1017 	cnex_soft_state_t *cnex_ssp;
1018 
1019 	switch (cmd) {
1020 	case DDI_ATTACH:
1021 		break;
1022 	case DDI_RESUME:
1023 		return (DDI_SUCCESS);
1024 	default:
1025 		return (DDI_FAILURE);
1026 	}
1027 
1028 	/*
1029 	 * Get the instance specific soft state structure.
1030 	 * Save the devi for this instance in the soft_state data.
1031 	 */
1032 	instance = ddi_get_instance(devi);
1033 	if (ddi_soft_state_zalloc(cnex_state, instance) != DDI_SUCCESS)
1034 		return (DDI_FAILURE);
1035 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
1036 
1037 	cnex_ssp->devi = devi;
1038 	cnex_ssp->clist = NULL;
1039 
1040 	if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
1041 	    "reg", (caddr_t)&reg_p, &reglen) != DDI_SUCCESS) {
1042 		return (DDI_FAILURE);
1043 	}
1044 
1045 	/* get the sun4v config handle for this device */
1046 	cnex_ssp->cfghdl = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
1047 	kmem_free(reg_p, reglen);
1048 
1049 	D1("cnex_attach: cfghdl=0x%llx\n", cnex_ssp->cfghdl);
1050 
1051 	/* init channel list mutex */
1052 	mutex_init(&cnex_ssp->clist_lock, NULL, MUTEX_DRIVER, NULL);
1053 
1054 	/* Register with LDC module */
1055 	cinfo.dip = devi;
1056 	cinfo.reg_chan = cnex_reg_chan;
1057 	cinfo.unreg_chan = cnex_unreg_chan;
1058 	cinfo.add_intr = cnex_add_intr;
1059 	cinfo.rem_intr = cnex_rem_intr;
1060 	cinfo.clr_intr = cnex_clr_intr;
1061 
1062 	/*
1063 	 * LDC register will fail if an nexus instance had already
1064 	 * registered with the LDC framework
1065 	 */
1066 	rv = ldc_register(&cinfo);
1067 	if (rv) {
1068 		DWARN("cnex_attach: unable to register with LDC\n");
1069 		ddi_soft_state_free(cnex_state, instance);
1070 		mutex_destroy(&cnex_ssp->clist_lock);
1071 		return (DDI_FAILURE);
1072 	}
1073 
1074 	if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
1075 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
1076 		ddi_remove_minor_node(devi, NULL);
1077 		ddi_soft_state_free(cnex_state, instance);
1078 		mutex_destroy(&cnex_ssp->clist_lock);
1079 		return (DDI_FAILURE);
1080 	}
1081 
1082 	/* Add interrupt redistribution callback. */
1083 	intr_dist_add_weighted(cnex_intr_redist, cnex_ssp);
1084 
1085 	ddi_report_dev(devi);
1086 	return (DDI_SUCCESS);
1087 }
1088 
1089 /*ARGSUSED*/
1090 static int
1091 cnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
1092 {
1093 	int 		instance;
1094 	ldc_cnex_t	cinfo;
1095 	cnex_soft_state_t *cnex_ssp;
1096 
1097 	switch (cmd) {
1098 	case DDI_DETACH:
1099 		break;
1100 	case DDI_SUSPEND:
1101 		return (DDI_SUCCESS);
1102 	default:
1103 		return (DDI_FAILURE);
1104 	}
1105 
1106 	instance = ddi_get_instance(devi);
1107 	cnex_ssp = ddi_get_soft_state(cnex_state, instance);
1108 
1109 	/* check if there are any channels still registered */
1110 	if (cnex_ssp->clist) {
1111 		cmn_err(CE_WARN, "?cnex_dettach: channels registered %d\n",
1112 		    ddi_get_instance(devi));
1113 		return (DDI_FAILURE);
1114 	}
1115 
1116 	/* Unregister with LDC module */
1117 	cinfo.dip = devi;
1118 	(void) ldc_unregister(&cinfo);
1119 
1120 	/* Remove interrupt redistribution callback. */
1121 	intr_dist_rem_weighted(cnex_intr_redist, cnex_ssp);
1122 
1123 	/* destroy mutex */
1124 	mutex_destroy(&cnex_ssp->clist_lock);
1125 
1126 	/* free soft state structure */
1127 	ddi_soft_state_free(cnex_state, instance);
1128 
1129 	return (DDI_SUCCESS);
1130 }
1131 
1132 /*ARGSUSED*/
1133 static int
1134 cnex_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1135 {
1136 	int instance;
1137 
1138 	if (otyp != OTYP_CHR)
1139 		return (EINVAL);
1140 
1141 	instance = getminor(*devp);
1142 	if (ddi_get_soft_state(cnex_state, instance) == NULL)
1143 		return (ENXIO);
1144 
1145 	return (0);
1146 }
1147 
1148 /*ARGSUSED*/
1149 static int
1150 cnex_close(dev_t dev, int flags, int otyp, cred_t *credp)
1151 {
1152 	int instance;
1153 
1154 	if (otyp != OTYP_CHR)
1155 		return (EINVAL);
1156 
1157 	instance = getminor(dev);
1158 	if (ddi_get_soft_state(cnex_state, instance) == NULL)
1159 		return (ENXIO);
1160 
1161 	return (0);
1162 }
1163 
1164 /*ARGSUSED*/
1165 static int
1166 cnex_ioctl(dev_t dev,
1167     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
1168 {
1169 	int instance;
1170 	cnex_soft_state_t *cnex_ssp;
1171 
1172 	instance = getminor(dev);
1173 	if ((cnex_ssp = ddi_get_soft_state(cnex_state, instance)) == NULL)
1174 		return (ENXIO);
1175 	ASSERT(cnex_ssp->devi);
1176 	return (ndi_devctl_ioctl(cnex_ssp->devi, cmd, arg, mode, 0));
1177 }
1178 
1179 static int
1180 cnex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
1181     void *arg, void *result)
1182 {
1183 	char		name[MAXNAMELEN];
1184 	uint32_t	reglen;
1185 	int		*cnex_regspec;
1186 
1187 	switch (ctlop) {
1188 	case DDI_CTLOPS_REPORTDEV:
1189 		if (rdip == NULL)
1190 			return (DDI_FAILURE);
1191 		cmn_err(CE_CONT, "?channel-device: %s%d\n",
1192 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
1193 		return (DDI_SUCCESS);
1194 
1195 	case DDI_CTLOPS_INITCHILD:
1196 	{
1197 		dev_info_t *child = (dev_info_t *)arg;
1198 
1199 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1200 		    DDI_PROP_DONTPASS, "reg",
1201 		    &cnex_regspec, &reglen) != DDI_SUCCESS) {
1202 			return (DDI_FAILURE);
1203 		}
1204 
1205 		(void) snprintf(name, sizeof (name), "%x", *cnex_regspec);
1206 		ddi_set_name_addr(child, name);
1207 		ddi_set_parent_data(child, NULL);
1208 		ddi_prop_free(cnex_regspec);
1209 		return (DDI_SUCCESS);
1210 	}
1211 
1212 	case DDI_CTLOPS_UNINITCHILD:
1213 	{
1214 		dev_info_t *child = (dev_info_t *)arg;
1215 
1216 		NDI_CONFIG_DEBUG((CE_NOTE,
1217 		    "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)",
1218 		    ddi_driver_name(child), DEVI(child)->devi_instance));
1219 
1220 		ddi_set_name_addr(child, NULL);
1221 
1222 		return (DDI_SUCCESS);
1223 	}
1224 
1225 	case DDI_CTLOPS_DMAPMAPC:
1226 	case DDI_CTLOPS_REPORTINT:
1227 	case DDI_CTLOPS_REGSIZE:
1228 	case DDI_CTLOPS_NREGS:
1229 	case DDI_CTLOPS_SIDDEV:
1230 	case DDI_CTLOPS_SLAVEONLY:
1231 	case DDI_CTLOPS_AFFINITY:
1232 	case DDI_CTLOPS_POKE:
1233 	case DDI_CTLOPS_PEEK:
1234 		/*
1235 		 * These ops correspond to functions that "shouldn't" be called
1236 		 * by a channel-device driver.  So we whine when we're called.
1237 		 */
1238 		cmn_err(CE_WARN, "%s%d: invalid op (%d) from %s%d\n",
1239 		    ddi_driver_name(dip), ddi_get_instance(dip), ctlop,
1240 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
1241 		return (DDI_FAILURE);
1242 
1243 	case DDI_CTLOPS_ATTACH:
1244 	case DDI_CTLOPS_BTOP:
1245 	case DDI_CTLOPS_BTOPR:
1246 	case DDI_CTLOPS_DETACH:
1247 	case DDI_CTLOPS_DVMAPAGESIZE:
1248 	case DDI_CTLOPS_IOMIN:
1249 	case DDI_CTLOPS_POWER:
1250 	case DDI_CTLOPS_PTOB:
1251 	default:
1252 		/*
1253 		 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
1254 		 */
1255 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
1256 	}
1257 }
1258 
1259 /*
1260  * cnex_find_chan_dip -- Find the dip of a device that is corresponding
1261  * 	to the specific channel. Below are the details on how the dip
1262  *	is derived.
1263  *
1264  *	- In the MD, the cfg-handle is expected to be unique for
1265  *	  virtual-device nodes that have the same 'name' property value.
1266  *	  This value is expected to be the same as that of "reg" property
1267  *	  of the corresponding OBP device node.
1268  *
1269  *	- The value of the 'name' property of a virtual-device node
1270  *	  in the MD is expected to be the same for the corresponding
1271  *	  OBP device node.
1272  *
1273  *	- Find the virtual-device node corresponding to a channel-endpoint
1274  *	  by walking backwards. Then obtain the values for the 'name' and
1275  *	  'cfg-handle' properties.
1276  *
1277  *	- Walk all the children of the cnex, find a matching dip which
1278  *	  has the same 'name' and 'reg' property values.
1279  *
1280  *	- The channels that have no corresponding device driver are
1281  *	  treated as if they  correspond to the cnex driver,
1282  *	  that is, return cnex dip for them. This means, the
1283  *	  cnex acts as an umbrella device driver. Note, this is
1284  *	  for 'intrstat' statistics purposes only. As a result of this,
1285  *	  the 'intrstat' shows cnex as the device that is servicing the
1286  *	  interrupts corresponding to these channels.
1287  *
1288  *	  For now, only one such case is known, that is, the channels that
1289  *	  are used by the "domain-services".
1290  */
1291 static dev_info_t *
1292 cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id,
1293     md_t *mdp, mde_cookie_t mde)
1294 {
1295 	int listsz;
1296 	int num_nodes;
1297 	int num_devs;
1298 	uint64_t cfghdl;
1299 	char *md_name;
1300 	mde_cookie_t *listp;
1301 	dev_info_t *cdip = NULL;
1302 
1303 	num_nodes = md_node_count(mdp);
1304 	ASSERT(num_nodes > 0);
1305 	listsz = num_nodes * sizeof (mde_cookie_t);
1306 	listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP);
1307 
1308 	num_devs = md_scan_dag(mdp, mde, md_find_name(mdp, "virtual-device"),
1309 	    md_find_name(mdp, "back"), listp);
1310 	ASSERT(num_devs <= 1);
1311 	if (num_devs <= 0) {
1312 		DWARN("cnex_find_chan_dip:channel(0x%llx): "
1313 		    "No virtual-device found\n", chan_id);
1314 		goto fdip_exit;
1315 	}
1316 	if (md_get_prop_str(mdp, listp[0], "name", &md_name) != 0) {
1317 		DWARN("cnex_find_chan_dip:channel(0x%llx): "
1318 		    "name property not found\n", chan_id);
1319 		goto fdip_exit;
1320 	}
1321 
1322 	D1("cnex_find_chan_dip: channel(0x%llx): virtual-device "
1323 	    "name property value = %s\n", chan_id, md_name);
1324 
1325 	if (md_get_prop_val(mdp, listp[0], "cfg-handle", &cfghdl) != 0) {
1326 		DWARN("cnex_find_chan_dip:channel(0x%llx): virtual-device's "
1327 		    "cfg-handle property not found\n", chan_id);
1328 		goto fdip_exit;
1329 	}
1330 
1331 	D1("cnex_find_chan_dip:channel(0x%llx): virtual-device cfg-handle "
1332 	    " property value = 0x%x\n", chan_id, cfghdl);
1333 
1334 	for (cdip = ddi_get_child(dip); cdip != NULL;
1335 	    cdip = ddi_get_next_sibling(cdip)) {
1336 
1337 		int *cnex_regspec;
1338 		uint32_t reglen;
1339 		char	*dev_name;
1340 
1341 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip,
1342 		    DDI_PROP_DONTPASS, "name",
1343 		    &dev_name) != DDI_PROP_SUCCESS) {
1344 			DWARN("cnex_find_chan_dip: name property not"
1345 			    " found for dip(0x%p)\n", cdip);
1346 			continue;
1347 		}
1348 		if (strcmp(md_name, dev_name) != 0) {
1349 			ddi_prop_free(dev_name);
1350 			continue;
1351 		}
1352 		ddi_prop_free(dev_name);
1353 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
1354 		    DDI_PROP_DONTPASS, "reg",
1355 		    &cnex_regspec, &reglen) != DDI_SUCCESS) {
1356 			DWARN("cnex_find_chan_dip: reg property not"
1357 			    " found for dip(0x%p)\n", cdip);
1358 			continue;
1359 		}
1360 		if (*cnex_regspec == cfghdl) {
1361 			D1("cnex_find_chan_dip:channel(0x%llx): found "
1362 			    "dip(0x%p) drvname=%s\n", chan_id, cdip,
1363 			    ddi_driver_name(cdip));
1364 			ddi_prop_free(cnex_regspec);
1365 			break;
1366 		}
1367 		ddi_prop_free(cnex_regspec);
1368 	}
1369 
1370 fdip_exit:
1371 	if (cdip == NULL) {
1372 		/*
1373 		 * If a virtual-device node exists but no dip found,
1374 		 * then for now print a DEBUG error message only.
1375 		 */
1376 		if (num_devs > 0) {
1377 			DERR("cnex_find_chan_dip:channel(0x%llx): "
1378 			    "No device found\n", chan_id);
1379 		}
1380 
1381 		/* If no dip was found, return cnex device's dip. */
1382 		cdip = dip;
1383 	}
1384 
1385 	kmem_free(listp, listsz);
1386 	D1("cnex_find_chan_dip:channel(0x%llx): returning dip=0x%p\n",
1387 	    chan_id, cdip);
1388 	return (cdip);
1389 }
1390 
1391 /* -------------------------------------------------------------------------- */
1392