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