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