xref: /titanic_51/usr/src/uts/common/io/nxge/nxge_intr.c (revision c389c7f85e1e94d7078f66fbb37362b0387243d0)
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 2009 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 
734 	if (nxge->ldgvp == NULL)
735 		goto nxge_hio_intr_uninit_exit;
736 
737 	control = nxge->ldgvp;
738 	if (control->ldgp) {
739 		KMEM_FREE(control->ldgp,
740 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
741 		control->ldgp = 0;
742 	}
743 
744 	if (control->ldvp) {
745 		KMEM_FREE(control->ldvp,
746 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
747 		control->ldvp = 0;
748 	}
749 
750 nxge_hio_intr_uninit_exit:
751 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit"));
752 }
753 
754 /*
755  * nxge_hio_tdsv_add
756  *
757  *	Add a transmit device interrupt.
758  *
759  * Arguments:
760  * 	nxge
761  * 	dc	The TDC whose interrupt we're adding
762  *
763  * Notes:
764  *
765  * Context:
766  *	Guest domain
767  */
768 static
769 hv_rv_t
770 nxge_hio_tdsv_add(
771 	nxge_t *nxge,
772 	nxge_hio_dc_t *dc)
773 {
774 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
775 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
776 	nxhv_dc_fp_t *tx = &nhd->hio.tx;
777 	hv_rv_t hv_rv;
778 
779 	if (tx->getinfo == 0) {
780 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
781 		    "nx_hio_tdsv_add: tx->getinfo absent"));
782 		return (EINVAL);
783 	}
784 
785 	/*
786 	 * Get the dma channel information.
787 	 */
788 	hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
789 	    &dc->ldg.ldsv);
790 	if (hv_rv != 0) {
791 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
792 		    "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
793 		return (EIO);
794 	}
795 
796 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
797 	    "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
798 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
799 
800 	if (hardware->tdc.count == 0) {
801 		hardware->tdc.start = dc->channel;
802 	}
803 
804 	hardware->tdc.count++;
805 	hardware->tdc.owned++;
806 
807 	/*
808 	 * In version 1.0 of the hybrid I/O driver, there
809 	 * are eight interrupt vectors per VR.
810 	 *
811 	 * Vectors 0 - 3 are reserved for RDCs.
812 	 * Vectors 4 - 7 are reserved for TDCs.
813 	 */
814 	dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE;
815 	// Version 1.0 hack only!
816 
817 	return (0);
818 }
819 
820 /*
821  * nxge_hio_rdsv_add
822  *
823  *	Add a transmit device interrupt.
824  *
825  * Arguments:
826  * 	nxge
827  * 	dc	The RDC whose interrupt we're adding
828  *
829  * Notes:
830  *
831  * Context:
832  *	Guest domain
833  */
834 static
835 hv_rv_t
836 nxge_hio_rdsv_add(
837 	nxge_t *nxge,
838 	nxge_hio_dc_t *dc)
839 {
840 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
841 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
842 	nxhv_dc_fp_t *rx = &nhd->hio.rx;
843 	hv_rv_t hv_rv;
844 
845 	if (rx->getinfo == 0) {
846 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
847 		    "nx_hio_tdsv_add: rx->getinfo absent"));
848 		return (EINVAL);
849 	}
850 
851 	/*
852 	 * Get DMA channel information.
853 	 */
854 	hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
855 	    &dc->ldg.ldsv);
856 	if (hv_rv != 0) {
857 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
858 		    "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
859 		return (EIO);
860 	}
861 
862 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
863 	    "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
864 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
865 
866 	if (hardware->max_rdcs == 0) {
867 		hardware->start_rdc = dc->channel;
868 		hardware->def_rdc = dc->channel;
869 	}
870 
871 	hardware->max_rdcs++;
872 
873 	/*
874 	 * In version 1.0 of the hybrid I/O driver, there
875 	 * are eight interrupt vectors per VR.
876 	 *
877 	 * Vectors 0 - 3 are reserved for RDCs.
878 	 */
879 	dc->ldg.vector = (dc->ldg.ldsv % 2);
880 	// Version 1.0 hack only!
881 
882 	return (0);
883 }
884 
885 /*
886  * nxge_hio_ldsv_add
887  *
888  *	Add a transmit or receive interrupt.
889  *
890  * Arguments:
891  * 	nxge
892  * 	dc	The DMA channel whose interrupt we're adding
893  *
894  * Notes:
895  *	Guest domains can only add interrupts for DMA channels.
896  *	They cannot access the MAC, MIF, or SYSERR interrupts.
897  *
898  * Context:
899  *	Guest domain
900  */
901 hv_rv_t
902 nxge_hio_ldsv_add(
903 	nxge_t *nxge,
904 	nxge_hio_dc_t *dc)
905 {
906 	nxge_ldgv_t *control;
907 	nxge_ldg_t *group;
908 	nxge_ldv_t *device;
909 	hv_rv_t hv_rv;
910 
911 	if (dc->type == VP_BOUND_TX) {
912 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)",
913 		    dc->channel));
914 		if ((hv_rv = nxge_hio_tdsv_add(nxge, dc)) != 0)
915 			return (hv_rv);
916 	} else {
917 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)",
918 		    dc->channel));
919 		if ((hv_rv = nxge_hio_rdsv_add(nxge, dc)) != 0)
920 			return (hv_rv);
921 	}
922 
923 	dc->ldg.map |= (1 << dc->ldg.ldsv);
924 
925 	control = nxge->ldgvp;
926 	if (control == NULL) {
927 		control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP);
928 		nxge->ldgvp = control;
929 		control->maxldgs = 1;
930 		control->maxldvs = 1;
931 		control->ldgp = KMEM_ZALLOC(
932 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP);
933 		control->ldvp = KMEM_ZALLOC(
934 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP);
935 	} else {
936 		control->maxldgs++;
937 		control->maxldvs++;
938 	}
939 
940 	/*
941 	 * Initialize the logical device group data structure first.
942 	 */
943 	group = &control->ldgp[dc->ldg.vector];
944 
945 	(void) memset(group, 0, sizeof (*group));
946 
947 	/*
948 	 * <hw_config.ldg> is a copy of the "interrupts" property.
949 	 */
950 	group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector];
951 	group->vldg_index = (uint8_t)dc->ldg.index;
952 	/*
953 	 * Since <vldg_index> is a dead variable, I'm reusing
954 	 * it in Hybrid I/O to calculate the offset into the
955 	 * virtual PIO_LDSV space.
956 	 */
957 
958 	group->arm = B_TRUE;
959 	group->ldg_timer = NXGE_TIMER_LDG;
960 	group->func = nxge->function_num;
961 	group->vector = dc->ldg.vector;
962 	/*
963 	 * <intdata> appears to be a dead variable.
964 	 * Though it is not used anywhere in the driver,
965 	 * we'll set it anyway.
966 	 */
967 	group->intdata = SID_DATA(group->func, group->vector);
968 
969 	group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */
970 	group->nxgep = nxge;
971 
972 	/*
973 	 * Initialize the logical device state vector next.
974 	 */
975 	device = &control->ldvp[dc->ldg.ldsv];
976 
977 	device->ldg_assigned = group->ldg;
978 	device->ldv = dc->ldg.ldsv;
979 
980 	if (dc->type == VP_BOUND_TX) {
981 		device->is_txdma = B_TRUE;
982 		device->is_rxdma = B_FALSE;
983 		device->ldv_intr_handler = nxge_tx_intr;
984 	} else {
985 		device->is_rxdma = B_TRUE;
986 		device->is_txdma = B_FALSE;
987 		device->ldv_intr_handler = nxge_rx_intr;
988 	}
989 	device->is_mif = B_FALSE;
990 	device->is_mac = B_FALSE;
991 	device->is_syserr = B_FALSE;
992 	device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */
993 
994 	device->channel = dc->channel;
995 	device->vdma_index = dc->page;
996 	device->func = nxge->function_num;
997 	device->ldgp = group;
998 	device->ldv_flags = 0;
999 	device->ldv_ldf_masks = 0;
1000 
1001 	device->nxgep = nxge;
1002 
1003 	/*
1004 	 * This code seems to imply a strict 1-to-1 correspondence.
1005 	 */
1006 	group->nldvs++;
1007 	group->ldvp = device;
1008 
1009 	control->nldvs++;
1010 	control->ldg_intrs++;
1011 
1012 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add"));
1013 
1014 	return (0);
1015 }
1016 
1017 /*
1018  * nxge_hio_ldsv_im
1019  *
1020  *	Manage a VLDG's interrupts.
1021  *
1022  * Arguments:
1023  * 	nxge
1024  * 	group	The VLDG to manage
1025  *
1026  * Notes:
1027  *	There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
1028  *	That sums to 256 bytes of virtual PIO_LDSV space.
1029  *
1030  *		VLDG0 starts at offset 0,
1031  *		VLDG1 starts at offset 32, etc.
1032  *
1033  *	Each set consists of 4 registers:
1034  *		Logical Device State Vector 0. LDSV0
1035  *		Logical Device State Vector 1. LDSV1
1036  *		Logical Device State Vector 2. LDSV2
1037  *		Logical Device Group Interrupt Management. LDGIMGN
1038  *
1039  *	The first three (LDSVx) are read-only.  The 4th register is the
1040  *	LDGIMGN, the LDG Interrupt Management register, which is used to
1041  *	arm the LDG, or set its timer.
1042  *
1043  *	The offset to write to is calculated as follows:
1044  *
1045  *		0x2000 + (VLDG << 4) + offset, where:
1046  *		VDLG is the virtual group, i.e., index of the LDG.
1047  *		offset is the offset (alignment 8) of the register
1048  *		       to read or write.
1049  *
1050  *	So, for example, if we wanted to arm the first TDC of VRx, we would
1051  *	calculate the address as:
1052  *
1053  *	0x2000 + (0 << 4) + 0x18 = 0x18
1054  *
1055  * Context:
1056  *	Guest domain
1057  *
1058  */
1059 void
1060 nxge_hio_ldsv_im(
1061 	/* Read any register in the PIO_LDSV space. */
1062 	nxge_t *nxge,
1063 	nxge_ldg_t *group,
1064 	pio_ld_op_t op,
1065 	uint64_t *value)
1066 {
1067 	uint64_t offset = VLDG_OFFSET;
1068 
1069 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1070 	offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */
1071 
1072 	NXGE_REG_RD64(nxge->npi_handle, offset, value);
1073 }
1074 
1075 void
1076 nxge_hio_ldgimgn(
1077 	/* Write the PIO_LDGIMGN register. */
1078 	nxge_t *nxge,
1079 	nxge_ldg_t *group)
1080 {
1081 	uint64_t offset = VLDG_OFFSET;
1082 	ldgimgm_t mgm;
1083 
1084 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1085 	offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */
1086 
1087 	mgm.value = 0;
1088 	if (group->arm) {
1089 		mgm.bits.ldw.arm = 1;
1090 		mgm.bits.ldw.timer = group->ldg_timer;
1091 	} else {
1092 		mgm.bits.ldw.arm = 0;
1093 		mgm.bits.ldw.timer = 0;
1094 	}
1095 	NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value);
1096 }
1097