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