xref: /illumos-gate/usr/src/uts/common/io/nxge/nxge_intr.c (revision 45d3dd981abb9025d8ac994cf4cc8ce8cb1a9480)
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(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 	/*
385 	 * Note: RDC interrupts will be armed in nxge_m_start(). This
386 	 * prevents us from getting an interrupt before we are ready
387 	 * to process packets.
388 	 */
389 	if (type == VP_BOUND_TX) {
390 		nxge_hio_ldgimgn(nxge, group);
391 	}
392 
393 	dc->interrupting = B_TRUE;
394 
395 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add"));
396 
397 	return (NXGE_OK);
398 }
399 
400 /*
401  * nxge_hio_intr_remove
402  *
403  *	Remove <channel>'s interrupt.
404  *
405  * Arguments:
406  * 	nxge
407  * 	type	Tx or Rx
408  * 	channel	The channel whose interrupt we want to remove.
409  *
410  * Notes:
411  *
412  * Context:
413  *	Guest domain
414  *
415  */
416 nxge_status_t
417 nxge_hio_intr_remove(
418 	nxge_t *nxge,
419 	vpc_type_t type,
420 	int channel)
421 {
422 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
423 	nxge_intr_t	*interrupts; /* The global interrupt data. */
424 	nxge_ldg_t	*group;	/* The logical device group data. */
425 
426 	int		vector;	/* A shorthand variable */
427 	int		status1, status2;
428 
429 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
430 
431 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
432 	    "==> nxge_hio_intr_remove(%cDC %d)", c, channel));
433 
434 	if (nxge->ldgvp == 0) {
435 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
436 		    "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel));
437 		return (NXGE_ERROR);
438 	}
439 
440 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
441 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
442 		    "nxge_hio_intr_remove(%cDC %d): DC FIND failed",
443 		    c, channel));
444 		return (NXGE_ERROR);
445 	}
446 
447 	if (dc->interrupting == B_FALSE) {
448 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
449 		    "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE",
450 		    c, channel));
451 		return (NXGE_OK);
452 	}
453 
454 	/* 'nxge_intr_type' is a bad name for this data structure. */
455 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
456 
457 	/* Set <vector> here to make the following code easier to read. */
458 	vector = dc->ldg.vector;
459 
460 	group = &nxge->ldgvp->ldgp[vector];
461 
462 	/* Disarm the interrupt. */
463 	group->arm = B_FALSE;
464 	nxge_hio_ldgimgn(nxge, group);
465 	group->arm = B_TRUE;	/* HIOXXX There IS a better way */
466 
467 	status1 = DDI_SUCCESS;
468 
469 	/* Disable the interrupt. */
470 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
471 	    != DDI_SUCCESS) {
472 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
473 		    "nxge_hio_intr_remove(%cDC %d): "
474 		    "ddi_intr_disable(%d) returned %s",
475 		    c, channel, vector, nxge_ddi_perror(status2)));
476 		status1 += status2;
477 	}
478 
479 	/* Remove the interrupt handler. */
480 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
481 	    != DDI_SUCCESS) {
482 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
483 		    "nxge_hio_intr_remove(%cDC %d): "
484 		    "ddi_intr_remove_handle(%d) returned %s",
485 		    c, channel, vector, nxge_ddi_perror(status2)));
486 		status1 += status2;
487 	}
488 
489 	if (status1 == DDI_SUCCESS) {
490 		dc->interrupting = B_FALSE;
491 
492 		interrupts->intr_added--;
493 		if (interrupts->intr_added == 0)
494 			interrupts->intr_enabled = B_FALSE;
495 	}
496 
497 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove"));
498 
499 	return (NXGE_OK);
500 }
501 
502 /*
503  * nxge_hio_intr_init
504  *
505  *	Initialize interrupts in a guest domain.
506  *
507  * Arguments:
508  * 	nxge
509  *
510  * Notes:
511  *
512  * Context:
513  *	Guest domain
514  *
515  */
516 nxge_status_t
517 nxge_hio_intr_init(
518 	nxge_t *nxge)
519 {
520 	int		*prop_val;
521 	uint_t		prop_len;
522 
523 	nxge_intr_t	*interrupts;
524 
525 	int		intr_type, behavior;
526 	int		nintrs, navail, nactual;
527 	int		inum = 0;
528 	int		ddi_status = DDI_SUCCESS;
529 
530 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
531 	int i;
532 
533 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init"));
534 
535 	/* Look up the "interrupts" property. */
536 	if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0,
537 	    "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) {
538 		NXGE_ERROR_MSG((nxge, HIO_CTL,
539 		    "==> nxge_hio_intr_init(obp): no 'interrupts' property"));
540 		return (NXGE_ERROR);
541 	}
542 
543 	/*
544 	 * For each device assigned, the content of each interrupts
545 	 * property is its logical device group.
546 	 *
547 	 * Assignment of interrupts property is in the the following
548 	 * order:
549 	 *
550 	 * two receive channels
551 	 * two transmit channels
552 	 */
553 	for (i = 0; i < prop_len; i++) {
554 		hardware->ldg[i] = prop_val[i];
555 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
556 		    "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d",
557 		    nxge->function_num, i, hardware->ldg[i]));
558 	}
559 	ddi_prop_free(prop_val);
560 
561 	hardware->max_grpids = prop_len;
562 	hardware->max_ldgs = prop_len;
563 	hardware->ldg_chn_start = 0;
564 
565 	/* ----------------------------------------------------- */
566 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
567 
568 	interrupts->intr_registered = B_FALSE;
569 	interrupts->intr_enabled = B_FALSE;
570 	interrupts->start_inum = 0;
571 
572 	ddi_status = ddi_intr_get_supported_types(
573 	    nxge->dip, &interrupts->intr_types);
574 	if (ddi_status != DDI_SUCCESS) {
575 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
576 		    "ddi_intr_get_supported_types() returned 0x%x, "
577 		    "types = 0x%x", ddi_status, interrupts->intr_types));
578 		return (NXGE_ERROR);
579 	}
580 
581 	NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() "
582 	    "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types));
583 
584 	/* HIOXXX hack */
585 	interrupts->intr_type = DDI_INTR_TYPE_FIXED;
586 	/* HIOXXX hack */
587 
588 	intr_type = interrupts->intr_type;
589 
590 	ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail);
591 	if (ddi_status != DDI_SUCCESS) {
592 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
593 		    "ddi_intr_get_navail() returned %s, navail: %d",
594 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
595 		    "DDI_INTR_NOTFOUND", navail));
596 		return (NXGE_ERROR);
597 	}
598 
599 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
600 	    "nxge_hio_intr_init: number of available interrupts: %d", navail));
601 
602 	ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs);
603 	if (ddi_status != DDI_SUCCESS) {
604 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
605 		    "ddi_intr_get_nintrs() returned %s, nintrs: %d",
606 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
607 		    "DDI_INTR_NOTFOUND", nintrs));
608 		return (NXGE_ERROR);
609 	}
610 
611 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
612 	    "nxge_hio_intr_init: number of interrupts: %d", nintrs));
613 
614 	interrupts->intr_size = navail * sizeof (ddi_intr_handle_t);
615 	interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP);
616 
617 	/*
618 	 * When <behavior> is set to  DDI_INTR_ALLOC_STRICT,
619 	 * ddi_intr_alloc() succeeds if and only if <navail>
620 	 * interrupts are are allocated. Otherwise, it fails.
621 	 */
622 	behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ?
623 	    DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL);
624 
625 	ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type,
626 	    inum, navail, &nactual, behavior);
627 	if (ddi_status != DDI_SUCCESS) {
628 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
629 		    "ddi_intr_alloc() returned 0x%x%, "
630 		    "number allocated: %d", ddi_status, nactual));
631 		return (NXGE_ERROR);
632 	}
633 
634 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
635 	    "nxge_hio_intr_init: number of interrupts allocated: %d", nactual));
636 
637 	/* <ninterrupts> is a dead variable: we may as well use it. */
638 	hardware->ninterrupts = nactual;
639 
640 	/* FOI: Get the interrupt priority. */
641 	if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0],
642 	    (uint_t *)&interrupts->pri)) != DDI_SUCCESS) {
643 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
644 		    " ddi_intr_get_pri() failed: %d", ddi_status));
645 	}
646 
647 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
648 	    "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri));
649 
650 	/* FOI: Get our interrupt capability flags. */
651 	if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0],
652 	    &interrupts->intr_cap)) != DDI_SUCCESS) {
653 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
654 		    "ddi_intr_get_cap() failed: %d", ddi_status));
655 	}
656 
657 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
658 	    "nxge_hio_intr_init: interrupt capabilities: %d",
659 	    interrupts->intr_cap));
660 
661 	interrupts->intr_registered = B_TRUE;
662 
663 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init"));
664 
665 	return (NXGE_OK);
666 }
667 
668 /*
669  * nxge_hio_intr_uninit
670  *
671  *	Uninitialize interrupts in a guest domain.
672  *
673  * Arguments:
674  * 	nxge
675  *
676  * Notes:
677  *
678  * Context:
679  *	Guest domain
680  */
681 void
682 nxge_hio_intr_uninit(
683 	nxge_t *nxge)
684 {
685 	nxge_hw_pt_cfg_t *hardware;
686 	nxge_intr_t *interrupts;
687 	nxge_ldgv_t *control;
688 	int i;
689 
690 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit"));
691 
692 	/* ----------------------------------------------------- */
693 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
694 
695 	/*
696 	 * If necessary, disable any currently active interrupts.
697 	 */
698 	if (interrupts->intr_enabled) {
699 		nxge_grp_set_t *set;
700 		nxge_grp_t *group;
701 		int channel;
702 
703 		set = &nxge->tx_set;
704 		group = set->group[0];	/* Assumption: only one group! */
705 		for (channel = 0; channel < NXGE_MAX_TDCS; channel++) {
706 			if ((1 << channel) & group->map) {
707 				(void) nxge_hio_intr_remove(
708 				    nxge, VP_BOUND_TX, channel);
709 			}
710 		}
711 
712 		set = &nxge->rx_set;
713 		group = set->group[0];	/* Assumption: only one group! */
714 		for (channel = 0; channel < NXGE_MAX_RDCS; channel++) {
715 			if ((1 << channel) & group->map) {
716 				(void) nxge_hio_intr_remove(
717 				    nxge, VP_BOUND_RX, channel);
718 			}
719 		}
720 	}
721 
722 	/*
723 	 * Free all of our allocated interrupts.
724 	 */
725 	hardware = &nxge->pt_config.hw_config;
726 	for (i = 0; i < hardware->ninterrupts; i++) {
727 		if (interrupts->htable[i])
728 			(void) ddi_intr_free(interrupts->htable[i]);
729 		interrupts->htable[i] = 0;
730 	}
731 
732 	interrupts->intr_registered = B_FALSE;
733 	KMEM_FREE(interrupts->htable, interrupts->intr_size);
734 	interrupts->htable = NULL;
735 
736 	if (nxge->ldgvp == NULL)
737 		goto nxge_hio_intr_uninit_exit;
738 
739 	control = nxge->ldgvp;
740 	if (control->ldgp) {
741 		KMEM_FREE(control->ldgp,
742 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
743 		control->ldgp = 0;
744 	}
745 
746 	if (control->ldvp) {
747 		KMEM_FREE(control->ldvp,
748 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
749 		control->ldvp = 0;
750 	}
751 
752 	KMEM_FREE(control, sizeof (nxge_ldgv_t));
753 	nxge->ldgvp = NULL;
754 
755 nxge_hio_intr_uninit_exit:
756 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit"));
757 }
758 
759 /*
760  * nxge_hio_tdsv_add
761  *
762  *	Add a transmit device interrupt.
763  *
764  * Arguments:
765  * 	nxge
766  * 	dc	The TDC whose interrupt we're adding
767  *
768  * Notes:
769  *
770  * Context:
771  *	Guest domain
772  */
773 static
774 hv_rv_t
775 nxge_hio_tdsv_add(
776 	nxge_t *nxge,
777 	nxge_hio_dc_t *dc)
778 {
779 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
780 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
781 	nxhv_dc_fp_t *tx = &nhd->hio.tx;
782 	hv_rv_t hv_rv;
783 
784 	if (tx->getinfo == 0) {
785 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
786 		    "nx_hio_tdsv_add: tx->getinfo absent"));
787 		return (EINVAL);
788 	}
789 
790 	/*
791 	 * Get the dma channel information.
792 	 */
793 	hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
794 	    &dc->ldg.ldsv);
795 	if (hv_rv != 0) {
796 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
797 		    "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
798 		return (EIO);
799 	}
800 
801 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
802 	    "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
803 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
804 
805 	if (hardware->tdc.count == 0) {
806 		hardware->tdc.start = dc->channel;
807 	}
808 
809 	hardware->tdc.count++;
810 	hardware->tdc.owned++;
811 
812 	/*
813 	 * In version 1.0 of the hybrid I/O driver, there
814 	 * are eight interrupt vectors per VR.
815 	 *
816 	 * Vectors 0 - 3 are reserved for RDCs.
817 	 * Vectors 4 - 7 are reserved for TDCs.
818 	 */
819 	dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE;
820 	// Version 1.0 hack only!
821 
822 	return (0);
823 }
824 
825 /*
826  * nxge_hio_rdsv_add
827  *
828  *	Add a transmit device interrupt.
829  *
830  * Arguments:
831  * 	nxge
832  * 	dc	The RDC whose interrupt we're adding
833  *
834  * Notes:
835  *
836  * Context:
837  *	Guest domain
838  */
839 static
840 hv_rv_t
841 nxge_hio_rdsv_add(
842 	nxge_t *nxge,
843 	nxge_hio_dc_t *dc)
844 {
845 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
846 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
847 	nxhv_dc_fp_t *rx = &nhd->hio.rx;
848 	hv_rv_t hv_rv;
849 
850 	if (rx->getinfo == 0) {
851 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
852 		    "nx_hio_tdsv_add: rx->getinfo absent"));
853 		return (EINVAL);
854 	}
855 
856 	/*
857 	 * Get DMA channel information.
858 	 */
859 	hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
860 	    &dc->ldg.ldsv);
861 	if (hv_rv != 0) {
862 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
863 		    "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
864 		return (EIO);
865 	}
866 
867 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
868 	    "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
869 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
870 
871 	if (hardware->max_rdcs == 0) {
872 		hardware->start_rdc = dc->channel;
873 		hardware->def_rdc = dc->channel;
874 	}
875 
876 	hardware->max_rdcs++;
877 
878 	/*
879 	 * In version 1.0 of the hybrid I/O driver, there
880 	 * are eight interrupt vectors per VR.
881 	 *
882 	 * Vectors 0 - 3 are reserved for RDCs.
883 	 */
884 	dc->ldg.vector = (dc->ldg.ldsv % 2);
885 	// Version 1.0 hack only!
886 
887 	return (0);
888 }
889 
890 /*
891  * nxge_hio_ldsv_add
892  *
893  *	Add a transmit or receive interrupt.
894  *
895  * Arguments:
896  * 	nxge
897  * 	dc	The DMA channel whose interrupt we're adding
898  *
899  * Notes:
900  *	Guest domains can only add interrupts for DMA channels.
901  *	They cannot access the MAC, MIF, or SYSERR interrupts.
902  *
903  * Context:
904  *	Guest domain
905  */
906 int
907 nxge_hio_ldsv_add(nxge_t *nxge, nxge_hio_dc_t *dc)
908 {
909 	nxge_ldgv_t *control;
910 	nxge_ldg_t *group;
911 	nxge_ldv_t *device;
912 
913 	if (dc->type == VP_BOUND_TX) {
914 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)",
915 		    dc->channel));
916 		if (nxge_hio_tdsv_add(nxge, dc) != 0)
917 			return (EIO);
918 	} else {
919 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)",
920 		    dc->channel));
921 		if (nxge_hio_rdsv_add(nxge, dc) != 0)
922 			return (EIO);
923 	}
924 
925 	dc->ldg.map |= (1 << dc->ldg.ldsv);
926 
927 	control = nxge->ldgvp;
928 	if (control == NULL) {
929 		control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP);
930 		nxge->ldgvp = control;
931 		control->maxldgs = 1;
932 		control->maxldvs = 1;
933 		control->ldgp = KMEM_ZALLOC(
934 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP);
935 		control->ldvp = KMEM_ZALLOC(
936 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP);
937 	} else {
938 		control->maxldgs++;
939 		control->maxldvs++;
940 	}
941 
942 	/*
943 	 * Initialize the logical device group data structure first.
944 	 */
945 	group = &control->ldgp[dc->ldg.vector];
946 
947 	(void) memset(group, 0, sizeof (*group));
948 
949 	/*
950 	 * <hw_config.ldg> is a copy of the "interrupts" property.
951 	 */
952 	group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector];
953 	group->vldg_index = (uint8_t)dc->ldg.index;
954 	/*
955 	 * Since <vldg_index> is a dead variable, I'm reusing
956 	 * it in Hybrid I/O to calculate the offset into the
957 	 * virtual PIO_LDSV space.
958 	 */
959 
960 	group->arm = B_TRUE;
961 	group->ldg_timer = NXGE_TIMER_LDG;
962 	group->func = nxge->function_num;
963 	group->vector = dc->ldg.vector;
964 	/*
965 	 * <intdata> appears to be a dead variable.
966 	 * Though it is not used anywhere in the driver,
967 	 * we'll set it anyway.
968 	 */
969 	group->intdata = SID_DATA(group->func, group->vector);
970 
971 	group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */
972 	group->nxgep = nxge;
973 
974 	/*
975 	 * Initialize the logical device state vector next.
976 	 */
977 	device = &control->ldvp[dc->ldg.ldsv];
978 
979 	device->ldg_assigned = group->ldg;
980 	device->ldv = dc->ldg.ldsv;
981 
982 	if (dc->type == VP_BOUND_TX) {
983 		device->is_txdma = B_TRUE;
984 		device->is_rxdma = B_FALSE;
985 		device->ldv_intr_handler = nxge_tx_intr;
986 	} else {
987 		device->is_rxdma = B_TRUE;
988 		device->is_txdma = B_FALSE;
989 		device->ldv_intr_handler = nxge_rx_intr;
990 	}
991 	device->is_mif = B_FALSE;
992 	device->is_mac = B_FALSE;
993 	device->is_syserr = B_FALSE;
994 	device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */
995 
996 	device->channel = dc->channel;
997 	device->vdma_index = dc->page;
998 	device->func = nxge->function_num;
999 	device->ldgp = group;
1000 	device->ldv_flags = 0;
1001 	device->ldv_ldf_masks = 0;
1002 
1003 	device->nxgep = nxge;
1004 
1005 	/*
1006 	 * This code seems to imply a strict 1-to-1 correspondence.
1007 	 */
1008 	group->nldvs++;
1009 	group->ldvp = device;
1010 
1011 	control->nldvs++;
1012 	control->ldg_intrs++;
1013 
1014 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add"));
1015 
1016 	return (0);
1017 }
1018 
1019 /*
1020  * nxge_hio_ldsv_im
1021  *
1022  *	Manage a VLDG's interrupts.
1023  *
1024  * Arguments:
1025  * 	nxge
1026  * 	group	The VLDG to manage
1027  *
1028  * Notes:
1029  *	There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
1030  *	That sums to 256 bytes of virtual PIO_LDSV space.
1031  *
1032  *		VLDG0 starts at offset 0,
1033  *		VLDG1 starts at offset 32, etc.
1034  *
1035  *	Each set consists of 4 registers:
1036  *		Logical Device State Vector 0. LDSV0
1037  *		Logical Device State Vector 1. LDSV1
1038  *		Logical Device State Vector 2. LDSV2
1039  *		Logical Device Group Interrupt Management. LDGIMGN
1040  *
1041  *	The first three (LDSVx) are read-only.  The 4th register is the
1042  *	LDGIMGN, the LDG Interrupt Management register, which is used to
1043  *	arm the LDG, or set its timer.
1044  *
1045  *	The offset to write to is calculated as follows:
1046  *
1047  *		0x2000 + (VLDG << 4) + offset, where:
1048  *		VDLG is the virtual group, i.e., index of the LDG.
1049  *		offset is the offset (alignment 8) of the register
1050  *		       to read or write.
1051  *
1052  *	So, for example, if we wanted to arm the first TDC of VRx, we would
1053  *	calculate the address as:
1054  *
1055  *	0x2000 + (0 << 4) + 0x18 = 0x18
1056  *
1057  * Context:
1058  *	Guest domain
1059  *
1060  */
1061 void
1062 nxge_hio_ldsv_im(
1063 	/* Read any register in the PIO_LDSV space. */
1064 	nxge_t *nxge,
1065 	nxge_ldg_t *group,
1066 	pio_ld_op_t op,
1067 	uint64_t *value)
1068 {
1069 	uint64_t offset = VLDG_OFFSET;
1070 
1071 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1072 	offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */
1073 
1074 	NXGE_REG_RD64(nxge->npi_handle, offset, value);
1075 }
1076 
1077 void
1078 nxge_hio_ldgimgn(
1079 	/* Write the PIO_LDGIMGN register. */
1080 	nxge_t *nxge,
1081 	nxge_ldg_t *group)
1082 {
1083 	uint64_t offset = VLDG_OFFSET;
1084 	ldgimgm_t mgm;
1085 
1086 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1087 	offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */
1088 
1089 	mgm.value = 0;
1090 	if (group->arm) {
1091 		mgm.bits.ldw.arm = 1;
1092 		mgm.bits.ldw.timer = group->ldg_timer;
1093 	} else {
1094 		mgm.bits.ldw.arm = 0;
1095 		mgm.bits.ldw.timer = 0;
1096 	}
1097 	NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value);
1098 }
1099