xref: /illumos-gate/usr/src/uts/common/io/nxge/nxge_intr.c (revision ca9327a6de44d69ddab3668cc1e143ce781387a3)
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 /*
23  * Copyright 2008 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  * nxge_intr.c
31  *
32  * This file manages the interrupts for a hybrid I/O (hio) device.
33  * In the future, it may manage interrupts for all Neptune-based
34  * devices.
35  *
36  */
37 
38 #include <sys/nxge/nxge_impl.h>
39 #include <sys/nxge/nxge_hio.h>
40 
41 /*
42  * External prototypes
43  */
44 
45 /* The following function may be found in nxge_[t|r]xdma.c */
46 extern uint_t nxge_tx_intr(void *, void *);
47 extern uint_t nxge_rx_intr(void *, void *);
48 
49 /*
50  * Local prototypes
51  */
52 static int nxge_intr_vec_find(nxge_t *, vpc_type_t, int);
53 
54 /*
55  * nxge_intr_add
56  *
57  *	Add <channel>'s interrupt.
58  *
59  * Arguments:
60  * 	nxge
61  * 	type	Tx or Rx
62  * 	channel	The channel whose interrupt we want to add.
63  *
64  * Notes:
65  *	Add here means: add a handler, enable, & arm the interrupt.
66  *
67  * Context:
68  *	Service domain
69  *
70  */
71 nxge_status_t
72 nxge_intr_add(
73 	nxge_t *nxge,
74 	vpc_type_t type,
75 	int channel)
76 {
77 	nxge_intr_t	*interrupts; /* The global interrupt data. */
78 	nxge_ldg_t	*group;	/* The logical device group data. */
79 	nxge_ldv_t	*ldvp;
80 
81 	uint_t		*inthandler; /* A parameter to ddi_intr_add_handler */
82 	int		vector;
83 	int		status1, status2;
84 
85 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
86 
87 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_add"));
88 
89 	if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
90 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
91 		    "nxge_intr_add(%cDC %d): vector not found", c, channel));
92 		return (NXGE_ERROR);
93 	}
94 
95 	ldvp = &nxge->ldgvp->ldvp[vector];
96 	group = ldvp->ldgp;
97 
98 	if (group->nldvs == 1) {
99 		inthandler = (uint_t *)group->ldvp->ldv_intr_handler;
100 	} else if (group->nldvs > 1) {
101 		inthandler = (uint_t *)group->sys_intr_handler;
102 	}
103 
104 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
105 
106 	status1 = DDI_SUCCESS;
107 
108 	if ((status2 = ddi_intr_add_handler(interrupts->htable[vector],
109 	    (ddi_intr_handler_t *)inthandler, group->ldvp, nxge))
110 	    != DDI_SUCCESS) {
111 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
112 		    "ddi_intr_add_handler(%d) returned %s",
113 		    c, channel, vector, nxge_ddi_perror(status2)));
114 		status1 += status2;
115 	}
116 
117 	interrupts->intr_added++;
118 
119 	/* Enable the interrupt. */
120 	if ((status2 = ddi_intr_enable(interrupts->htable[vector]))
121 	    != DDI_SUCCESS) {
122 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
123 		    "ddi_intr_enable(%d) returned %s",
124 		    c, channel, vector, nxge_ddi_perror(status2)));
125 		status1 += status2;
126 	}
127 
128 	if (status1 == DDI_SUCCESS) {
129 		interrupts->intr_enabled = B_TRUE;
130 
131 		/* Finally, arm the interrupt. */
132 		if (group->nldvs == 1) {
133 			npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
134 			(void) npi_intr_ldg_mgmt_set(handle, group->ldg,
135 			    B_TRUE, group->ldg_timer);
136 		}
137 	}
138 
139 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_add"));
140 
141 	return (NXGE_OK);
142 }
143 
144 /*
145  * nxge_intr_remove
146  *
147  *	Remove <channel>'s interrupt.
148  *
149  * Arguments:
150  * 	nxge
151  * 	type	Tx or Rx
152  * 	channel	The channel whose interrupt we want to remove.
153  *
154  * Notes:
155  *	Remove here means: disarm, disable, & remove the handler.
156  *
157  * Context:
158  *	Service domain
159  *
160  */
161 nxge_status_t
162 nxge_intr_remove(
163 	nxge_t *nxge,
164 	vpc_type_t type,
165 	int channel)
166 {
167 	nxge_intr_t	*interrupts; /* The global interrupt data. */
168 	nxge_ldg_t	*group;	/* The logical device group data. */
169 	nxge_ldv_t	*ldvp;
170 
171 	int		vector;
172 	int		status1, status2;
173 
174 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
175 
176 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_remove"));
177 
178 	if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
179 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
180 		    "nxge_intr_remove(%cDC %d): vector not found", c, channel));
181 		return (NXGE_ERROR);
182 	}
183 
184 	ldvp = &nxge->ldgvp->ldvp[vector];
185 	group = ldvp->ldgp;
186 
187 	/* Disarm the interrupt. */
188 	if (group->nldvs == 1) {
189 		npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
190 		group->arm = B_FALSE;
191 		(void) npi_intr_ldg_mgmt_set(handle, group->ldg,
192 		    B_TRUE, group->ldg_timer);
193 		group->arm = B_TRUE; /* HIOXXX There IS a better way */
194 	}
195 
196 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
197 
198 	status1 = DDI_SUCCESS;
199 
200 	/* Disable the interrupt. */
201 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
202 	    != DDI_SUCCESS) {
203 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
204 		    ": ddi_intr_disable(%d) returned %s",
205 		    c, channel, vector, nxge_ddi_perror(status2)));
206 		status1 += status2;
207 	}
208 
209 	/* Remove the interrupt handler. */
210 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
211 	    != DDI_SUCCESS) {
212 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
213 		    ": ddi_intr_remove_handler(%d) returned %s",
214 		    c, channel, vector, nxge_ddi_perror(status2)));
215 		status1 += status2;
216 	}
217 
218 	if (status1 == DDI_SUCCESS) {
219 		interrupts->intr_added--;
220 		if (interrupts->intr_added == 0)
221 			interrupts->intr_enabled = B_FALSE;
222 	}
223 
224 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_remove"));
225 
226 	return (NXGE_OK);
227 }
228 
229 /*
230  * nxge_intr_vec_find
231  *
232  *	Find the interrupt vector associated with <channel>.
233  *
234  * Arguments:
235  * 	nxge
236  * 	type	Tx or Rx
237  * 	channel	The channel whose vector we want to find.
238  *
239  * Notes:
240  *
241  * Context:
242  *	Service domain
243  *
244  */
245 static
246 int
247 nxge_intr_vec_find(
248 	nxge_t *nxge,
249 	vpc_type_t type,
250 	int channel)
251 {
252 	nxge_hw_pt_cfg_t *hardware;
253 	nxge_ldgv_t	*ldgvp;
254 	nxge_ldv_t	*ldvp;
255 
256 	int		first, limit, vector;
257 
258 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
259 	    "==> nxge_intr_vec_find(%cDC %d)",
260 	    type == VP_BOUND_TX ? 'T' : 'R', channel));
261 
262 	if (nxge->ldgvp == 0) {
263 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
264 		    "nxge_hio_intr_vec_find(%cDC %d): ldgvp == 0",
265 		    type == VP_BOUND_TX ? 'T' : 'R', channel));
266 		return (-1);
267 	}
268 
269 	hardware = &nxge->pt_config.hw_config;
270 
271 	first = hardware->ldg_chn_start;
272 	if (type == VP_BOUND_TX) {
273 		first += 8;	/* HIOXXX N2/NIU hack */
274 		limit = first + hardware->tdc.count;
275 	} else {
276 		limit = first + hardware->max_rdcs;
277 	}
278 
279 	ldgvp = nxge->ldgvp;
280 	for (vector = first; vector < limit; vector++) {
281 		ldvp = &ldgvp->ldvp[vector];
282 		if (ldvp->channel == channel)
283 			break;
284 	}
285 
286 	if (vector == limit) {
287 		return (-1);
288 	}
289 
290 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_vec_find"));
291 
292 	return (vector);
293 }
294 
295 /*
296  * ---------------------------------------------------------------------
297  * HIO-specific interrupt functions.
298  * ---------------------------------------------------------------------
299  */
300 
301 /*
302  * nxge_hio_intr_add
303  *
304  *	Add <channel>'s interrupt.
305  *
306  * Arguments:
307  * 	nxge
308  * 	type	Tx or Rx
309  * 	channel	The channel whose interrupt we want to remove.
310  *
311  * Notes:
312  *
313  * Context:
314  *	Guest domain
315  *
316  */
317 nxge_status_t
318 nxge_hio_intr_add(
319 	nxge_t *nxge,
320 	vpc_type_t type,
321 	int channel)
322 {
323 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
324 	nxge_intr_t	*interrupts; /* The global interrupt data. */
325 	nxge_ldg_t	*group;	/* The logical device group data. */
326 	uint_t		*inthandler; /* A parameter to ddi_intr_add_handler */
327 
328 	int		vector;	/* A shorthand variable */
329 	int		ddi_status; /* The response to ddi_intr_add_handler */
330 
331 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
332 
333 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
334 	    "==> nxge_hio_intr_add(%cDC %d)", c, channel));
335 
336 	if (nxge->ldgvp == 0) {
337 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
338 		    "nxge_hio_intr_add(%cDC %d): ldgvp == 0", c, channel));
339 		return (NXGE_ERROR);
340 	}
341 
342 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
343 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
344 		    "nxge_hio_intr_add: find(%s, %d) failed", c, channel));
345 		return (NXGE_ERROR);
346 	}
347 
348 	/* 'nxge_intr_type' is a bad name for this data structure. */
349 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
350 
351 	/* Set <vector> here to make the following code easier to read. */
352 	vector = dc->ldg.vector;
353 
354 	group = &nxge->ldgvp->ldgp[vector];
355 
356 	if (group->nldvs == 1) {
357 		inthandler = (uint_t *)group->ldvp->ldv_intr_handler;
358 	} else if (group->nldvs > 1) {
359 		inthandler = (uint_t *)group->sys_intr_handler;
360 	}
361 
362 	if ((ddi_status = ddi_intr_add_handler(interrupts->htable[vector],
363 	    (ddi_intr_handler_t *)inthandler, group->ldvp, nxge))
364 	    != DDI_SUCCESS) {
365 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
366 		    "nxge_hio_intr_add(%cDC %d): "
367 		    "ddi_intr_add_handler(%d) returned %s",
368 		    c, channel, vector, nxge_ddi_perror(ddi_status)));
369 		return (NXGE_ERROR);
370 	}
371 
372 	interrupts->intr_added++;
373 
374 	/* Enable the interrupt. */
375 	if ((ddi_status = ddi_intr_enable(interrupts->htable[vector]))
376 	    != DDI_SUCCESS) {
377 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
378 		    "nxge_hio_intr_add(%cDC %d): "
379 		    "ddi_intr_enable(%d) returned %s",
380 		    c, channel, vector, nxge_ddi_perror(ddi_status)));
381 		return (NXGE_ERROR);
382 	}
383 
384 	interrupts->intr_enabled = B_TRUE;
385 
386 	/* Finally, arm the interrupt. */
387 	nxge_hio_ldgimgn(nxge, group);
388 
389 	dc->interrupting = B_TRUE;
390 
391 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add"));
392 
393 	return (NXGE_OK);
394 }
395 
396 /*
397  * nxge_hio_intr_remove
398  *
399  *	Remove <channel>'s interrupt.
400  *
401  * Arguments:
402  * 	nxge
403  * 	type	Tx or Rx
404  * 	channel	The channel whose interrupt we want to remove.
405  *
406  * Notes:
407  *
408  * Context:
409  *	Guest domain
410  *
411  */
412 nxge_status_t
413 nxge_hio_intr_remove(
414 	nxge_t *nxge,
415 	vpc_type_t type,
416 	int channel)
417 {
418 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
419 	nxge_intr_t	*interrupts; /* The global interrupt data. */
420 	nxge_ldg_t	*group;	/* The logical device group data. */
421 
422 	int		vector;	/* A shorthand variable */
423 	int		status1, status2;
424 
425 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
426 
427 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
428 	    "==> nxge_hio_intr_remove(%cDC %d)", c, channel));
429 
430 	if (nxge->ldgvp == 0) {
431 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
432 		    "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel));
433 		return (NXGE_ERROR);
434 	}
435 
436 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
437 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
438 		    "nxge_hio_intr_remove(%cDC %d): DC FIND failed",
439 		    c, channel));
440 		return (NXGE_ERROR);
441 	}
442 
443 	if (dc->interrupting == B_FALSE) {
444 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
445 		    "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE",
446 		    c, channel));
447 		return (NXGE_OK);
448 	}
449 
450 	/* 'nxge_intr_type' is a bad name for this data structure. */
451 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
452 
453 	/* Set <vector> here to make the following code easier to read. */
454 	vector = dc->ldg.vector;
455 
456 	group = &nxge->ldgvp->ldgp[vector];
457 
458 	/* Disarm the interrupt. */
459 	group->arm = B_FALSE;
460 	nxge_hio_ldgimgn(nxge, group);
461 	group->arm = B_TRUE;	/* HIOXXX There IS a better way */
462 
463 	status1 = DDI_SUCCESS;
464 
465 	/* Disable the interrupt. */
466 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
467 	    != DDI_SUCCESS) {
468 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
469 		    "nxge_hio_intr_remove(%cDC %d): "
470 		    "ddi_intr_disable(%d) returned %s",
471 		    c, channel, vector, nxge_ddi_perror(status2)));
472 		status1 += status2;
473 	}
474 
475 	/* Remove the interrupt handler. */
476 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
477 	    != DDI_SUCCESS) {
478 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
479 		    "nxge_hio_intr_remove(%cDC %d): "
480 		    "ddi_intr_remove_handle(%d) returned %s",
481 		    c, channel, vector, nxge_ddi_perror(status2)));
482 		status1 += status2;
483 	}
484 
485 	if (status1 == DDI_SUCCESS) {
486 		dc->interrupting = B_FALSE;
487 
488 		interrupts->intr_added--;
489 		if (interrupts->intr_added == 0)
490 			interrupts->intr_enabled = B_FALSE;
491 	}
492 
493 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove"));
494 
495 	return (NXGE_OK);
496 }
497 
498 /*
499  * nxge_hio_intr_init
500  *
501  *	Initialize interrupts in a guest domain.
502  *
503  * Arguments:
504  * 	nxge
505  *
506  * Notes:
507  *
508  * Context:
509  *	Guest domain
510  *
511  */
512 nxge_status_t
513 nxge_hio_intr_init(
514 	nxge_t *nxge)
515 {
516 	int		*prop_val;
517 	uint_t		prop_len;
518 
519 	nxge_intr_t	*interrupts;
520 
521 	int		intr_type, behavior;
522 	int		nintrs, navail, nactual;
523 	int		inum = 0;
524 	int		ddi_status = DDI_SUCCESS;
525 
526 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
527 	int i;
528 
529 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init"));
530 
531 	/* Look up the "interrupts" property. */
532 	if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0,
533 	    "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) {
534 		NXGE_ERROR_MSG((nxge, HIO_CTL,
535 		    "==> nxge_hio_intr_init(obp): no 'interrupts' property"));
536 		return (NXGE_ERROR);
537 	}
538 
539 	/*
540 	 * For each device assigned, the content of each interrupts
541 	 * property is its logical device group.
542 	 *
543 	 * Assignment of interrupts property is in the the following
544 	 * order:
545 	 *
546 	 * two receive channels
547 	 * two transmit channels
548 	 */
549 	for (i = 0; i < prop_len; i++) {
550 		hardware->ldg[i] = prop_val[i];
551 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
552 		    "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d",
553 		    nxge->function_num, i, hardware->ldg[i]));
554 	}
555 	ddi_prop_free(prop_val);
556 
557 	hardware->max_grpids = prop_len;
558 	hardware->max_ldgs = prop_len;
559 	hardware->ldg_chn_start = 0;
560 
561 	/* ----------------------------------------------------- */
562 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
563 
564 	interrupts->intr_registered = B_FALSE;
565 	interrupts->intr_enabled = B_FALSE;
566 	interrupts->start_inum = 0;
567 
568 	ddi_status = ddi_intr_get_supported_types(
569 	    nxge->dip, &interrupts->intr_types);
570 	if (ddi_status != DDI_SUCCESS) {
571 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
572 		    "ddi_intr_get_supported_types() returned 0x%x, "
573 		    "types = 0x%x", ddi_status, interrupts->intr_types));
574 		return (NXGE_ERROR);
575 	}
576 
577 	NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() "
578 	    "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types));
579 
580 	/* HIOXXX hack */
581 	interrupts->intr_type = DDI_INTR_TYPE_FIXED;
582 	/* HIOXXX hack */
583 
584 	intr_type = interrupts->intr_type;
585 
586 	ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail);
587 	if (ddi_status != DDI_SUCCESS) {
588 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
589 		    "ddi_intr_get_navail() returned %s, navail: %d",
590 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
591 		    "DDI_INTR_NOTFOUND", navail));
592 		return (NXGE_ERROR);
593 	}
594 
595 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
596 	    "nxge_hio_intr_init: number of available interrupts: %d", navail));
597 
598 	ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs);
599 	if (ddi_status != DDI_SUCCESS) {
600 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
601 		    "ddi_intr_get_nintrs() returned %s, nintrs: %d",
602 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
603 		    "DDI_INTR_NOTFOUND", nintrs));
604 		return (NXGE_ERROR);
605 	}
606 
607 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
608 	    "nxge_hio_intr_init: number of interrupts: %d", nintrs));
609 
610 	interrupts->intr_size = navail * sizeof (ddi_intr_handle_t);
611 	interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP);
612 
613 	/*
614 	 * When <behavior> is set to  DDI_INTR_ALLOC_STRICT,
615 	 * ddi_intr_alloc() succeeds if and only if <navail>
616 	 * interrupts are are allocated. Otherwise, it fails.
617 	 */
618 	behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ?
619 	    DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL);
620 
621 	ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type,
622 	    inum, navail, &nactual, behavior);
623 	if (ddi_status != DDI_SUCCESS) {
624 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
625 		    "ddi_intr_alloc() returned 0x%x%, "
626 		    "number allocated: %d", ddi_status, nactual));
627 		return (NXGE_ERROR);
628 	}
629 
630 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
631 	    "nxge_hio_intr_init: number of interrupts allocated: %d", nactual));
632 
633 	/* <ninterrupts> is a dead variable: we may as well use it. */
634 	hardware->ninterrupts = nactual;
635 
636 	/* FOI: Get the interrupt priority. */
637 	if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0],
638 	    (uint_t *)&interrupts->pri)) != DDI_SUCCESS) {
639 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
640 		    " ddi_intr_get_pri() failed: %d", ddi_status));
641 	}
642 
643 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
644 	    "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri));
645 
646 	/* FOI: Get our interrupt capability flags. */
647 	if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0],
648 	    &interrupts->intr_cap)) != DDI_SUCCESS) {
649 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
650 		    "ddi_intr_get_cap() failed: %d", ddi_status));
651 	}
652 
653 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
654 	    "nxge_hio_intr_init: interrupt capabilities: %d",
655 	    interrupts->intr_cap));
656 
657 	interrupts->intr_registered = B_TRUE;
658 
659 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init"));
660 
661 	return (NXGE_OK);
662 }
663 
664 /*
665  * nxge_hio_intr_uninit
666  *
667  *	Uninitialize interrupts in a guest domain.
668  *
669  * Arguments:
670  * 	nxge
671  *
672  * Notes:
673  *
674  * Context:
675  *	Guest domain
676  */
677 void
678 nxge_hio_intr_uninit(
679 	nxge_t *nxge)
680 {
681 	nxge_hw_pt_cfg_t *hardware;
682 	nxge_intr_t *interrupts;
683 	nxge_ldgv_t *control;
684 	int i;
685 
686 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit"));
687 
688 	/* ----------------------------------------------------- */
689 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
690 
691 	/*
692 	 * If necessary, disable any currently active interrupts.
693 	 */
694 	if (interrupts->intr_enabled) {
695 		nxge_grp_set_t *set;
696 		nxge_grp_t *group;
697 		int channel;
698 
699 		set = &nxge->tx_set;
700 		group = set->group[0];	/* Assumption: only one group! */
701 		for (channel = 0; channel < NXGE_MAX_TDCS; channel++) {
702 			if ((1 << channel) & group->map) {
703 				(void) nxge_hio_intr_remove(
704 				    nxge, VP_BOUND_TX, channel);
705 			}
706 		}
707 
708 		set = &nxge->rx_set;
709 		group = set->group[0];	/* Assumption: only one group! */
710 		for (channel = 0; channel < NXGE_MAX_RDCS; channel++) {
711 			if ((1 << channel) & group->map) {
712 				(void) nxge_hio_intr_remove(
713 				    nxge, VP_BOUND_RX, channel);
714 			}
715 		}
716 	}
717 
718 	/*
719 	 * Free all of our allocated interrupts.
720 	 */
721 	hardware = &nxge->pt_config.hw_config;
722 	for (i = 0; i < hardware->ninterrupts; i++) {
723 		if (interrupts->htable[i])
724 			(void) ddi_intr_free(interrupts->htable[i]);
725 		interrupts->htable[i] = 0;
726 	}
727 
728 	interrupts->intr_registered = B_FALSE;
729 
730 	control = nxge->ldgvp;
731 	if (control->ldgp) {
732 		KMEM_FREE(control->ldgp,
733 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
734 		control->ldgp = 0;
735 	}
736 
737 	if (control->ldvp) {
738 		KMEM_FREE(control->ldvp,
739 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
740 		control->ldvp = 0;
741 	}
742 
743 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit"));
744 }
745 
746 /*
747  * nxge_hio_tdsv_add
748  *
749  *	Add a transmit device interrupt.
750  *
751  * Arguments:
752  * 	nxge
753  * 	dc	The TDC whose interrupt we're adding
754  *
755  * Notes:
756  *
757  * Context:
758  *	Guest domain
759  */
760 static
761 hv_rv_t
762 nxge_hio_tdsv_add(
763 	nxge_t *nxge,
764 	nxge_hio_dc_t *dc)
765 {
766 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
767 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
768 	nxhv_dc_fp_t *tx = &nhd->hio.tx;
769 	hv_rv_t hv_rv;
770 
771 	if (tx->getinfo == 0) {
772 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
773 		    "nx_hio_tdsv_add: tx->getinfo absent"));
774 		return (EINVAL);
775 	}
776 
777 	/*
778 	 * Get the dma channel information.
779 	 */
780 	hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
781 	    &dc->ldg.ldsv);
782 	if (hv_rv != 0) {
783 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
784 		    "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
785 		return (EIO);
786 	}
787 
788 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
789 	    "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
790 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
791 
792 	if (hardware->tdc.count == 0) {
793 		hardware->tdc.start = dc->channel;
794 	}
795 
796 	hardware->tdc.count++;
797 	hardware->tdc.owned++;
798 
799 	/*
800 	 * In version 1.0 of the hybrid I/O driver, there
801 	 * are eight interrupt vectors per VR.
802 	 *
803 	 * Vectors 0 - 3 are reserved for RDCs.
804 	 * Vectors 4 - 7 are reserved for TDCs.
805 	 */
806 	dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE;
807 	// Version 1.0 hack only!
808 
809 	return (0);
810 }
811 
812 /*
813  * nxge_hio_rdsv_add
814  *
815  *	Add a transmit device interrupt.
816  *
817  * Arguments:
818  * 	nxge
819  * 	dc	The RDC whose interrupt we're adding
820  *
821  * Notes:
822  *
823  * Context:
824  *	Guest domain
825  */
826 static
827 hv_rv_t
828 nxge_hio_rdsv_add(
829 	nxge_t *nxge,
830 	nxge_hio_dc_t *dc)
831 {
832 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
833 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
834 	nxhv_dc_fp_t *rx = &nhd->hio.rx;
835 	hv_rv_t hv_rv;
836 
837 	if (rx->getinfo == 0) {
838 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
839 		    "nx_hio_tdsv_add: rx->getinfo absent"));
840 		return (EINVAL);
841 	}
842 
843 	/*
844 	 * Get DMA channel information.
845 	 */
846 	hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
847 	    &dc->ldg.ldsv);
848 	if (hv_rv != 0) {
849 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
850 		    "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
851 		return (EIO);
852 	}
853 
854 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
855 	    "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
856 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
857 
858 	if (hardware->max_rdcs == 0) {
859 		hardware->start_rdc = dc->channel;
860 		hardware->def_rdc = dc->channel;
861 	}
862 
863 	hardware->max_rdcs++;
864 
865 	/*
866 	 * In version 1.0 of the hybrid I/O driver, there
867 	 * are eight interrupt vectors per VR.
868 	 *
869 	 * Vectors 0 - 3 are reserved for RDCs.
870 	 */
871 	dc->ldg.vector = (dc->ldg.ldsv % 2);
872 	// Version 1.0 hack only!
873 
874 	return (0);
875 }
876 
877 /*
878  * nxge_hio_ldsv_add
879  *
880  *	Add a transmit or receive interrupt.
881  *
882  * Arguments:
883  * 	nxge
884  * 	dc	The DMA channel whose interrupt we're adding
885  *
886  * Notes:
887  *	Guest domains can only add interrupts for DMA channels.
888  *	They cannot access the MAC, MIF, or SYSERR interrupts.
889  *
890  * Context:
891  *	Guest domain
892  */
893 hv_rv_t
894 nxge_hio_ldsv_add(
895 	nxge_t *nxge,
896 	nxge_hio_dc_t *dc)
897 {
898 	nxge_ldgv_t *control;
899 	nxge_ldg_t *group;
900 	nxge_ldv_t *device;
901 	hv_rv_t hv_rv;
902 
903 	if (dc->type == VP_BOUND_TX) {
904 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)",
905 		    dc->channel));
906 		if ((hv_rv = nxge_hio_tdsv_add(nxge, dc)) != 0)
907 			return (hv_rv);
908 	} else {
909 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)",
910 		    dc->channel));
911 		if ((hv_rv = nxge_hio_rdsv_add(nxge, dc)) != 0)
912 			return (hv_rv);
913 	}
914 
915 	dc->ldg.map |= (1 << dc->ldg.ldsv);
916 
917 	control = nxge->ldgvp;
918 	if (control == NULL) {
919 		control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP);
920 		nxge->ldgvp = control;
921 		control->maxldgs = 1;
922 		control->maxldvs = 1;
923 		control->ldgp = KMEM_ZALLOC(
924 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP);
925 		control->ldvp = KMEM_ZALLOC(
926 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP);
927 	} else {
928 		control->maxldgs++;
929 		control->maxldvs++;
930 	}
931 
932 	/*
933 	 * Initialize the logical device group data structure first.
934 	 */
935 	group = &control->ldgp[dc->ldg.vector];
936 
937 	(void) memset(group, 0, sizeof (*group));
938 
939 	/*
940 	 * <hw_config.ldg> is a copy of the "interrupts" property.
941 	 */
942 	group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector];
943 	group->vldg_index = (uint8_t)dc->ldg.index;
944 	/*
945 	 * Since <vldg_index> is a dead variable, I'm reusing
946 	 * it in Hybrid I/O to calculate the offset into the
947 	 * virtual PIO_LDSV space.
948 	 */
949 
950 	group->arm = B_TRUE;
951 	group->ldg_timer = NXGE_TIMER_LDG;
952 	group->func = nxge->function_num;
953 	group->vector = dc->ldg.vector;
954 	/*
955 	 * <intdata> appears to be a dead variable.
956 	 * Though it is not used anywhere in the driver,
957 	 * we'll set it anyway.
958 	 */
959 	group->intdata = SID_DATA(group->func, group->vector);
960 
961 	group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */
962 	group->nxgep = nxge;
963 
964 	/*
965 	 * Initialize the logical device state vector next.
966 	 */
967 	device = &control->ldvp[dc->ldg.ldsv];
968 
969 	device->ldg_assigned = group->ldg;
970 	device->ldv = dc->ldg.ldsv;
971 
972 	if (dc->type == VP_BOUND_TX) {
973 		device->is_txdma = B_TRUE;
974 		device->is_rxdma = B_FALSE;
975 		device->ldv_intr_handler = nxge_tx_intr;
976 	} else {
977 		device->is_rxdma = B_TRUE;
978 		device->is_txdma = B_FALSE;
979 		device->ldv_intr_handler = nxge_rx_intr;
980 	}
981 	device->is_mif = B_FALSE;
982 	device->is_mac = B_FALSE;
983 	device->is_syserr = B_FALSE;
984 	device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */
985 
986 	device->channel = dc->channel;
987 	device->vdma_index = dc->page;
988 	device->func = nxge->function_num;
989 	device->ldgp = group;
990 	device->ldv_flags = 0;
991 	device->ldv_ldf_masks = 0;
992 
993 	device->nxgep = nxge;
994 
995 	/*
996 	 * This code seems to imply a strict 1-to-1 correspondence.
997 	 */
998 	group->nldvs++;
999 	group->ldvp = device;
1000 
1001 	control->nldvs++;
1002 	control->ldg_intrs++;
1003 
1004 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add"));
1005 
1006 	return (0);
1007 }
1008 
1009 /*
1010  * nxge_hio_ldsv_im
1011  *
1012  *	Manage a VLDG's interrupts.
1013  *
1014  * Arguments:
1015  * 	nxge
1016  * 	group	The VLDG to manage
1017  *
1018  * Notes:
1019  *	There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
1020  *	That sums to 256 bytes of virtual PIO_LDSV space.
1021  *
1022  *		VLDG0 starts at offset 0,
1023  *		VLDG1 starts at offset 32, etc.
1024  *
1025  *	Each set consists of 4 registers:
1026  *		Logical Device State Vector 0. LDSV0
1027  *		Logical Device State Vector 1. LDSV1
1028  *		Logical Device State Vector 2. LDSV2
1029  *		Logical Device Group Interrupt Management. LDGIMGN
1030  *
1031  *	The first three (LDSVx) are read-only.  The 4th register is the
1032  *	LDGIMGN, the LDG Interrupt Management register, which is used to
1033  *	arm the LDG, or set its timer.
1034  *
1035  *	The offset to write to is calculated as follows:
1036  *
1037  *		0x2000 + (VLDG << 4) + offset, where:
1038  *		VDLG is the virtual group, i.e., index of the LDG.
1039  *		offset is the offset (alignment 8) of the register
1040  *		       to read or write.
1041  *
1042  *	So, for example, if we wanted to arm the first TDC of VRx, we would
1043  *	calculate the address as:
1044  *
1045  *	0x2000 + (0 << 4) + 0x18 = 0x18
1046  *
1047  * Context:
1048  *	Guest domain
1049  *
1050  */
1051 void
1052 nxge_hio_ldsv_im(
1053 	/* Read any register in the PIO_LDSV space. */
1054 	nxge_t *nxge,
1055 	nxge_ldg_t *group,
1056 	pio_ld_op_t op,
1057 	uint64_t *value)
1058 {
1059 	uint64_t offset = VLDG_OFFSET;
1060 
1061 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1062 	offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */
1063 
1064 	NXGE_REG_RD64(nxge->npi_handle, offset, value);
1065 }
1066 
1067 void
1068 nxge_hio_ldgimgn(
1069 	/* Write the PIO_LDGIMGN register. */
1070 	nxge_t *nxge,
1071 	nxge_ldg_t *group)
1072 {
1073 	uint64_t offset = VLDG_OFFSET;
1074 	ldgimgm_t mgm;
1075 
1076 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1077 	offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */
1078 
1079 	mgm.value = 0;
1080 	if (group->arm) {
1081 		mgm.bits.ldw.arm = 1;
1082 		mgm.bits.ldw.timer = group->ldg_timer;
1083 	} else {
1084 		mgm.bits.ldw.arm = 0;
1085 		mgm.bits.ldw.timer = 0;
1086 	}
1087 	NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value);
1088 }
1089