xref: /illumos-gate/usr/src/uts/common/io/nxge/nxge_intr.c (revision 0dc2366f)
1678453a8Sspeer /*
2678453a8Sspeer  * CDDL HEADER START
3678453a8Sspeer  *
4678453a8Sspeer  * The contents of this file are subject to the terms of the
5678453a8Sspeer  * Common Development and Distribution License (the "License").
6678453a8Sspeer  * You may not use this file except in compliance with the License.
7678453a8Sspeer  *
8678453a8Sspeer  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9678453a8Sspeer  * or http://www.opensolaris.org/os/licensing.
10678453a8Sspeer  * See the License for the specific language governing permissions
11678453a8Sspeer  * and limitations under the License.
12678453a8Sspeer  *
13678453a8Sspeer  * When distributing Covered Code, include this CDDL HEADER in each
14678453a8Sspeer  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15678453a8Sspeer  * If applicable, add the following below this CDDL HEADER, with the
16678453a8Sspeer  * fields enclosed by brackets "[]" replaced with your own identifying
17678453a8Sspeer  * information: Portions Copyright [yyyy] [name of copyright owner]
18678453a8Sspeer  *
19678453a8Sspeer  * CDDL HEADER END
20678453a8Sspeer  */
21678453a8Sspeer 
22678453a8Sspeer /*
23*0dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24678453a8Sspeer  * Use is subject to license terms.
25678453a8Sspeer  */
26678453a8Sspeer 
27678453a8Sspeer /*
28678453a8Sspeer  * nxge_intr.c
29678453a8Sspeer  *
30678453a8Sspeer  * This file manages the interrupts for a hybrid I/O (hio) device.
31678453a8Sspeer  * In the future, it may manage interrupts for all Neptune-based
32678453a8Sspeer  * devices.
33678453a8Sspeer  *
34678453a8Sspeer  */
35678453a8Sspeer 
36678453a8Sspeer #include <sys/nxge/nxge_impl.h>
37678453a8Sspeer #include <sys/nxge/nxge_hio.h>
38678453a8Sspeer 
39678453a8Sspeer /*
40678453a8Sspeer  * External prototypes
41678453a8Sspeer  */
42678453a8Sspeer 
43678453a8Sspeer /* The following function may be found in nxge_[t|r]xdma.c */
44678453a8Sspeer extern uint_t nxge_tx_intr(void *, void *);
45678453a8Sspeer extern uint_t nxge_rx_intr(void *, void *);
46678453a8Sspeer 
47678453a8Sspeer /*
48678453a8Sspeer  * Local prototypes
49678453a8Sspeer  */
50678453a8Sspeer static int nxge_intr_vec_find(nxge_t *, vpc_type_t, int);
51678453a8Sspeer 
52678453a8Sspeer /*
53678453a8Sspeer  * nxge_intr_add
54678453a8Sspeer  *
55678453a8Sspeer  *	Add <channel>'s interrupt.
56678453a8Sspeer  *
57678453a8Sspeer  * Arguments:
58678453a8Sspeer  * 	nxge
59678453a8Sspeer  * 	type	Tx or Rx
60678453a8Sspeer  * 	channel	The channel whose interrupt we want to add.
61678453a8Sspeer  *
62678453a8Sspeer  * Notes:
63678453a8Sspeer  *	Add here means: add a handler, enable, & arm the interrupt.
64678453a8Sspeer  *
65678453a8Sspeer  * Context:
66678453a8Sspeer  *	Service domain
67678453a8Sspeer  *
68678453a8Sspeer  */
69678453a8Sspeer nxge_status_t
70678453a8Sspeer nxge_intr_add(
71678453a8Sspeer 	nxge_t *nxge,
72678453a8Sspeer 	vpc_type_t type,
73678453a8Sspeer 	int channel)
74678453a8Sspeer {
75678453a8Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
76678453a8Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
77678453a8Sspeer 	nxge_ldv_t	*ldvp;
78678453a8Sspeer 
79678453a8Sspeer 	uint_t		*inthandler; /* A parameter to ddi_intr_add_handler */
80678453a8Sspeer 	int		vector;
81678453a8Sspeer 	int		status1, status2;
82678453a8Sspeer 
83678453a8Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
84678453a8Sspeer 
85678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_add"));
86678453a8Sspeer 
87678453a8Sspeer 	if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
88678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
89678453a8Sspeer 		    "nxge_intr_add(%cDC %d): vector not found", c, channel));
90678453a8Sspeer 		return (NXGE_ERROR);
91678453a8Sspeer 	}
92678453a8Sspeer 
93678453a8Sspeer 	ldvp = &nxge->ldgvp->ldvp[vector];
94678453a8Sspeer 	group = ldvp->ldgp;
95678453a8Sspeer 
96678453a8Sspeer 	if (group->nldvs == 1) {
97678453a8Sspeer 		inthandler = (uint_t *)group->ldvp->ldv_intr_handler;
98678453a8Sspeer 	} else if (group->nldvs > 1) {
99678453a8Sspeer 		inthandler = (uint_t *)group->sys_intr_handler;
100678453a8Sspeer 	}
101678453a8Sspeer 
102678453a8Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
103678453a8Sspeer 
104678453a8Sspeer 	status1 = DDI_SUCCESS;
105678453a8Sspeer 
106678453a8Sspeer 	if ((status2 = ddi_intr_add_handler(interrupts->htable[vector],
107678453a8Sspeer 	    (ddi_intr_handler_t *)inthandler, group->ldvp, nxge))
108678453a8Sspeer 	    != DDI_SUCCESS) {
109678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
110678453a8Sspeer 		    "ddi_intr_add_handler(%d) returned %s",
111678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
112678453a8Sspeer 		status1 += status2;
113678453a8Sspeer 	}
114678453a8Sspeer 
115678453a8Sspeer 	interrupts->intr_added++;
116678453a8Sspeer 
117678453a8Sspeer 	/* Enable the interrupt. */
118678453a8Sspeer 	if ((status2 = ddi_intr_enable(interrupts->htable[vector]))
119678453a8Sspeer 	    != DDI_SUCCESS) {
120678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
121678453a8Sspeer 		    "ddi_intr_enable(%d) returned %s",
122678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
123678453a8Sspeer 		status1 += status2;
124678453a8Sspeer 	}
125678453a8Sspeer 
126678453a8Sspeer 	if (status1 == DDI_SUCCESS) {
127678453a8Sspeer 		interrupts->intr_enabled = B_TRUE;
128678453a8Sspeer 
129678453a8Sspeer 		/* Finally, arm the interrupt. */
130678453a8Sspeer 		if (group->nldvs == 1) {
131678453a8Sspeer 			npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
132678453a8Sspeer 			(void) npi_intr_ldg_mgmt_set(handle, group->ldg,
133678453a8Sspeer 			    B_TRUE, group->ldg_timer);
134678453a8Sspeer 		}
135678453a8Sspeer 	}
136678453a8Sspeer 
137678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_add"));
138678453a8Sspeer 
139678453a8Sspeer 	return (NXGE_OK);
140678453a8Sspeer }
141678453a8Sspeer 
142678453a8Sspeer /*
143678453a8Sspeer  * nxge_intr_remove
144678453a8Sspeer  *
145678453a8Sspeer  *	Remove <channel>'s interrupt.
146678453a8Sspeer  *
147678453a8Sspeer  * Arguments:
148678453a8Sspeer  * 	nxge
149678453a8Sspeer  * 	type	Tx or Rx
150678453a8Sspeer  * 	channel	The channel whose interrupt we want to remove.
151678453a8Sspeer  *
152678453a8Sspeer  * Notes:
153678453a8Sspeer  *	Remove here means: disarm, disable, & remove the handler.
154678453a8Sspeer  *
155678453a8Sspeer  * Context:
156678453a8Sspeer  *	Service domain
157678453a8Sspeer  *
158678453a8Sspeer  */
159678453a8Sspeer nxge_status_t
160678453a8Sspeer nxge_intr_remove(
161678453a8Sspeer 	nxge_t *nxge,
162678453a8Sspeer 	vpc_type_t type,
163678453a8Sspeer 	int channel)
164678453a8Sspeer {
165678453a8Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
166678453a8Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
167678453a8Sspeer 	nxge_ldv_t	*ldvp;
168678453a8Sspeer 
169678453a8Sspeer 	int		vector;
170678453a8Sspeer 	int		status1, status2;
171678453a8Sspeer 
172678453a8Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
173678453a8Sspeer 
174678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_remove"));
175678453a8Sspeer 
176678453a8Sspeer 	if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
177678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
178678453a8Sspeer 		    "nxge_intr_remove(%cDC %d): vector not found", c, channel));
179678453a8Sspeer 		return (NXGE_ERROR);
180678453a8Sspeer 	}
181678453a8Sspeer 
182678453a8Sspeer 	ldvp = &nxge->ldgvp->ldvp[vector];
183678453a8Sspeer 	group = ldvp->ldgp;
184678453a8Sspeer 
185678453a8Sspeer 	/* Disarm the interrupt. */
186678453a8Sspeer 	if (group->nldvs == 1) {
187678453a8Sspeer 		npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
188678453a8Sspeer 		group->arm = B_FALSE;
189678453a8Sspeer 		(void) npi_intr_ldg_mgmt_set(handle, group->ldg,
190678453a8Sspeer 		    B_TRUE, group->ldg_timer);
191678453a8Sspeer 		group->arm = B_TRUE; /* HIOXXX There IS a better way */
192678453a8Sspeer 	}
193678453a8Sspeer 
194678453a8Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
195678453a8Sspeer 
196678453a8Sspeer 	status1 = DDI_SUCCESS;
197678453a8Sspeer 
198678453a8Sspeer 	/* Disable the interrupt. */
199678453a8Sspeer 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
200678453a8Sspeer 	    != DDI_SUCCESS) {
201678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
202678453a8Sspeer 		    ": ddi_intr_disable(%d) returned %s",
203678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
204678453a8Sspeer 		status1 += status2;
205678453a8Sspeer 	}
206678453a8Sspeer 
207678453a8Sspeer 	/* Remove the interrupt handler. */
208678453a8Sspeer 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
209678453a8Sspeer 	    != DDI_SUCCESS) {
210678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
211678453a8Sspeer 		    ": ddi_intr_remove_handler(%d) returned %s",
212678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
213678453a8Sspeer 		status1 += status2;
214678453a8Sspeer 	}
215678453a8Sspeer 
216678453a8Sspeer 	if (status1 == DDI_SUCCESS) {
217678453a8Sspeer 		interrupts->intr_added--;
218678453a8Sspeer 		if (interrupts->intr_added == 0)
219678453a8Sspeer 			interrupts->intr_enabled = B_FALSE;
220678453a8Sspeer 	}
221678453a8Sspeer 
222678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_remove"));
223678453a8Sspeer 
224678453a8Sspeer 	return (NXGE_OK);
225678453a8Sspeer }
226678453a8Sspeer 
227678453a8Sspeer /*
228678453a8Sspeer  * nxge_intr_vec_find
229678453a8Sspeer  *
230678453a8Sspeer  *	Find the interrupt vector associated with <channel>.
231678453a8Sspeer  *
232678453a8Sspeer  * Arguments:
233678453a8Sspeer  * 	nxge
234678453a8Sspeer  * 	type	Tx or Rx
235678453a8Sspeer  * 	channel	The channel whose vector we want to find.
236678453a8Sspeer  *
237678453a8Sspeer  * Notes:
238678453a8Sspeer  *
239678453a8Sspeer  * Context:
240678453a8Sspeer  *	Service domain
241678453a8Sspeer  *
242678453a8Sspeer  */
243678453a8Sspeer static
244678453a8Sspeer int
245678453a8Sspeer nxge_intr_vec_find(
246678453a8Sspeer 	nxge_t *nxge,
247678453a8Sspeer 	vpc_type_t type,
248678453a8Sspeer 	int channel)
249678453a8Sspeer {
250678453a8Sspeer 	nxge_hw_pt_cfg_t *hardware;
251678453a8Sspeer 	nxge_ldgv_t	*ldgvp;
252678453a8Sspeer 	nxge_ldv_t	*ldvp;
253678453a8Sspeer 
254678453a8Sspeer 	int		first, limit, vector;
255678453a8Sspeer 
256678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
257678453a8Sspeer 	    "==> nxge_intr_vec_find(%cDC %d)",
258678453a8Sspeer 	    type == VP_BOUND_TX ? 'T' : 'R', channel));
259678453a8Sspeer 
260678453a8Sspeer 	if (nxge->ldgvp == 0) {
261678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
262678453a8Sspeer 		    "nxge_hio_intr_vec_find(%cDC %d): ldgvp == 0",
263678453a8Sspeer 		    type == VP_BOUND_TX ? 'T' : 'R', channel));
264678453a8Sspeer 		return (-1);
265678453a8Sspeer 	}
266678453a8Sspeer 
267678453a8Sspeer 	hardware = &nxge->pt_config.hw_config;
268678453a8Sspeer 
269678453a8Sspeer 	first = hardware->ldg_chn_start;
270678453a8Sspeer 	if (type == VP_BOUND_TX) {
271678453a8Sspeer 		first += 8;	/* HIOXXX N2/NIU hack */
272678453a8Sspeer 		limit = first + hardware->tdc.count;
273678453a8Sspeer 	} else {
274678453a8Sspeer 		limit = first + hardware->max_rdcs;
275678453a8Sspeer 	}
276678453a8Sspeer 
277678453a8Sspeer 	ldgvp = nxge->ldgvp;
278678453a8Sspeer 	for (vector = first; vector < limit; vector++) {
279678453a8Sspeer 		ldvp = &ldgvp->ldvp[vector];
280678453a8Sspeer 		if (ldvp->channel == channel)
281678453a8Sspeer 			break;
282678453a8Sspeer 	}
283678453a8Sspeer 
284678453a8Sspeer 	if (vector == limit) {
285678453a8Sspeer 		return (-1);
286678453a8Sspeer 	}
287678453a8Sspeer 
288678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_vec_find"));
289678453a8Sspeer 
290678453a8Sspeer 	return (vector);
291678453a8Sspeer }
292678453a8Sspeer 
293678453a8Sspeer /*
294678453a8Sspeer  * ---------------------------------------------------------------------
295678453a8Sspeer  * HIO-specific interrupt functions.
296678453a8Sspeer  * ---------------------------------------------------------------------
297678453a8Sspeer  */
298678453a8Sspeer 
299678453a8Sspeer /*
300678453a8Sspeer  * nxge_hio_intr_add
301678453a8Sspeer  *
302678453a8Sspeer  *	Add <channel>'s interrupt.
303678453a8Sspeer  *
304678453a8Sspeer  * Arguments:
305678453a8Sspeer  * 	nxge
306678453a8Sspeer  * 	type	Tx or Rx
307678453a8Sspeer  * 	channel	The channel whose interrupt we want to remove.
308678453a8Sspeer  *
309678453a8Sspeer  * Notes:
310678453a8Sspeer  *
311678453a8Sspeer  * Context:
312678453a8Sspeer  *	Guest domain
313678453a8Sspeer  *
314678453a8Sspeer  */
315678453a8Sspeer nxge_status_t
316678453a8Sspeer nxge_hio_intr_add(
317678453a8Sspeer 	nxge_t *nxge,
318678453a8Sspeer 	vpc_type_t type,
319678453a8Sspeer 	int channel)
320678453a8Sspeer {
321678453a8Sspeer 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
322678453a8Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
323678453a8Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
324678453a8Sspeer 	uint_t		*inthandler; /* A parameter to ddi_intr_add_handler */
325678453a8Sspeer 
326678453a8Sspeer 	int		vector;	/* A shorthand variable */
327678453a8Sspeer 	int		ddi_status; /* The response to ddi_intr_add_handler */
328678453a8Sspeer 
329678453a8Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
330678453a8Sspeer 
331678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
332678453a8Sspeer 	    "==> nxge_hio_intr_add(%cDC %d)", c, channel));
333678453a8Sspeer 
334678453a8Sspeer 	if (nxge->ldgvp == 0) {
335678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
336678453a8Sspeer 		    "nxge_hio_intr_add(%cDC %d): ldgvp == 0", c, channel));
337678453a8Sspeer 		return (NXGE_ERROR);
338678453a8Sspeer 	}
339678453a8Sspeer 
340678453a8Sspeer 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
341678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
342678453a8Sspeer 		    "nxge_hio_intr_add: find(%s, %d) failed", c, channel));
343678453a8Sspeer 		return (NXGE_ERROR);
344678453a8Sspeer 	}
345678453a8Sspeer 
346678453a8Sspeer 	/* 'nxge_intr_type' is a bad name for this data structure. */
347678453a8Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
348678453a8Sspeer 
349678453a8Sspeer 	/* Set <vector> here to make the following code easier to read. */
350678453a8Sspeer 	vector = dc->ldg.vector;
351678453a8Sspeer 
352678453a8Sspeer 	group = &nxge->ldgvp->ldgp[vector];
353678453a8Sspeer 
354678453a8Sspeer 	if (group->nldvs == 1) {
355678453a8Sspeer 		inthandler = (uint_t *)group->ldvp->ldv_intr_handler;
356678453a8Sspeer 	} else if (group->nldvs > 1) {
357678453a8Sspeer 		inthandler = (uint_t *)group->sys_intr_handler;
358678453a8Sspeer 	}
359678453a8Sspeer 
360678453a8Sspeer 	if ((ddi_status = ddi_intr_add_handler(interrupts->htable[vector],
361678453a8Sspeer 	    (ddi_intr_handler_t *)inthandler, group->ldvp, nxge))
362678453a8Sspeer 	    != DDI_SUCCESS) {
363678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
364678453a8Sspeer 		    "nxge_hio_intr_add(%cDC %d): "
365678453a8Sspeer 		    "ddi_intr_add_handler(%d) returned %s",
366678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(ddi_status)));
367678453a8Sspeer 		return (NXGE_ERROR);
368678453a8Sspeer 	}
369678453a8Sspeer 
370678453a8Sspeer 	interrupts->intr_added++;
371678453a8Sspeer 
372678453a8Sspeer 	/* Enable the interrupt. */
373678453a8Sspeer 	if ((ddi_status = ddi_intr_enable(interrupts->htable[vector]))
374678453a8Sspeer 	    != DDI_SUCCESS) {
375678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
376678453a8Sspeer 		    "nxge_hio_intr_add(%cDC %d): "
377678453a8Sspeer 		    "ddi_intr_enable(%d) returned %s",
378678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(ddi_status)));
379678453a8Sspeer 		return (NXGE_ERROR);
380678453a8Sspeer 	}
381678453a8Sspeer 
382678453a8Sspeer 	interrupts->intr_enabled = B_TRUE;
383678453a8Sspeer 
384e759c33aSMichael Speer 	/*
385e759c33aSMichael Speer 	 * Note: RDC interrupts will be armed in nxge_m_start(). This
386e759c33aSMichael Speer 	 * prevents us from getting an interrupt before we are ready
387e759c33aSMichael Speer 	 * to process packets.
388e759c33aSMichael Speer 	 */
389e759c33aSMichael Speer 	if (type == VP_BOUND_TX) {
390e759c33aSMichael Speer 		nxge_hio_ldgimgn(nxge, group);
391e759c33aSMichael Speer 	}
392678453a8Sspeer 
393678453a8Sspeer 	dc->interrupting = B_TRUE;
394678453a8Sspeer 
395678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add"));
396678453a8Sspeer 
397678453a8Sspeer 	return (NXGE_OK);
398678453a8Sspeer }
399678453a8Sspeer 
400678453a8Sspeer /*
401678453a8Sspeer  * nxge_hio_intr_remove
402678453a8Sspeer  *
403678453a8Sspeer  *	Remove <channel>'s interrupt.
404678453a8Sspeer  *
405678453a8Sspeer  * Arguments:
406678453a8Sspeer  * 	nxge
407678453a8Sspeer  * 	type	Tx or Rx
408678453a8Sspeer  * 	channel	The channel whose interrupt we want to remove.
409678453a8Sspeer  *
410678453a8Sspeer  * Notes:
411678453a8Sspeer  *
412678453a8Sspeer  * Context:
413678453a8Sspeer  *	Guest domain
414678453a8Sspeer  *
415678453a8Sspeer  */
416678453a8Sspeer nxge_status_t
417678453a8Sspeer nxge_hio_intr_remove(
418678453a8Sspeer 	nxge_t *nxge,
419678453a8Sspeer 	vpc_type_t type,
420678453a8Sspeer 	int channel)
421678453a8Sspeer {
422678453a8Sspeer 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
423678453a8Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
424678453a8Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
425678453a8Sspeer 
426678453a8Sspeer 	int		vector;	/* A shorthand variable */
427678453a8Sspeer 	int		status1, status2;
428678453a8Sspeer 
429678453a8Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
430678453a8Sspeer 
431678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
432678453a8Sspeer 	    "==> nxge_hio_intr_remove(%cDC %d)", c, channel));
433678453a8Sspeer 
434678453a8Sspeer 	if (nxge->ldgvp == 0) {
435678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
436678453a8Sspeer 		    "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel));
437678453a8Sspeer 		return (NXGE_ERROR);
438678453a8Sspeer 	}
439678453a8Sspeer 
440678453a8Sspeer 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
441678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
442678453a8Sspeer 		    "nxge_hio_intr_remove(%cDC %d): DC FIND failed",
443678453a8Sspeer 		    c, channel));
444678453a8Sspeer 		return (NXGE_ERROR);
445678453a8Sspeer 	}
446678453a8Sspeer 
447678453a8Sspeer 	if (dc->interrupting == B_FALSE) {
448678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
449678453a8Sspeer 		    "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE",
450678453a8Sspeer 		    c, channel));
451678453a8Sspeer 		return (NXGE_OK);
452678453a8Sspeer 	}
453678453a8Sspeer 
454678453a8Sspeer 	/* 'nxge_intr_type' is a bad name for this data structure. */
455678453a8Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
456678453a8Sspeer 
457678453a8Sspeer 	/* Set <vector> here to make the following code easier to read. */
458678453a8Sspeer 	vector = dc->ldg.vector;
459678453a8Sspeer 
460678453a8Sspeer 	group = &nxge->ldgvp->ldgp[vector];
461678453a8Sspeer 
462678453a8Sspeer 	/* Disarm the interrupt. */
463678453a8Sspeer 	group->arm = B_FALSE;
464678453a8Sspeer 	nxge_hio_ldgimgn(nxge, group);
465678453a8Sspeer 	group->arm = B_TRUE;	/* HIOXXX There IS a better way */
466678453a8Sspeer 
467678453a8Sspeer 	status1 = DDI_SUCCESS;
468678453a8Sspeer 
469678453a8Sspeer 	/* Disable the interrupt. */
470678453a8Sspeer 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
471678453a8Sspeer 	    != DDI_SUCCESS) {
472678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
473678453a8Sspeer 		    "nxge_hio_intr_remove(%cDC %d): "
474678453a8Sspeer 		    "ddi_intr_disable(%d) returned %s",
475678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
476678453a8Sspeer 		status1 += status2;
477678453a8Sspeer 	}
478678453a8Sspeer 
479678453a8Sspeer 	/* Remove the interrupt handler. */
480678453a8Sspeer 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
481678453a8Sspeer 	    != DDI_SUCCESS) {
482678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
483678453a8Sspeer 		    "nxge_hio_intr_remove(%cDC %d): "
484678453a8Sspeer 		    "ddi_intr_remove_handle(%d) returned %s",
485678453a8Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
486678453a8Sspeer 		status1 += status2;
487678453a8Sspeer 	}
488678453a8Sspeer 
489678453a8Sspeer 	if (status1 == DDI_SUCCESS) {
490678453a8Sspeer 		dc->interrupting = B_FALSE;
491678453a8Sspeer 
492678453a8Sspeer 		interrupts->intr_added--;
493678453a8Sspeer 		if (interrupts->intr_added == 0)
494678453a8Sspeer 			interrupts->intr_enabled = B_FALSE;
495678453a8Sspeer 	}
496678453a8Sspeer 
497678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove"));
498678453a8Sspeer 
499678453a8Sspeer 	return (NXGE_OK);
500678453a8Sspeer }
501678453a8Sspeer 
502678453a8Sspeer /*
503678453a8Sspeer  * nxge_hio_intr_init
504678453a8Sspeer  *
505678453a8Sspeer  *	Initialize interrupts in a guest domain.
506678453a8Sspeer  *
507678453a8Sspeer  * Arguments:
508678453a8Sspeer  * 	nxge
509678453a8Sspeer  *
510678453a8Sspeer  * Notes:
511678453a8Sspeer  *
512678453a8Sspeer  * Context:
513678453a8Sspeer  *	Guest domain
514678453a8Sspeer  *
515678453a8Sspeer  */
516678453a8Sspeer nxge_status_t
517678453a8Sspeer nxge_hio_intr_init(
518678453a8Sspeer 	nxge_t *nxge)
519678453a8Sspeer {
520678453a8Sspeer 	int		*prop_val;
521678453a8Sspeer 	uint_t		prop_len;
522678453a8Sspeer 
523678453a8Sspeer 	nxge_intr_t	*interrupts;
524678453a8Sspeer 
525678453a8Sspeer 	int		intr_type, behavior;
526678453a8Sspeer 	int		nintrs, navail, nactual;
527678453a8Sspeer 	int		inum = 0;
528678453a8Sspeer 	int		ddi_status = DDI_SUCCESS;
529678453a8Sspeer 
530678453a8Sspeer 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
531678453a8Sspeer 	int i;
532678453a8Sspeer 
533678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init"));
534678453a8Sspeer 
535678453a8Sspeer 	/* Look up the "interrupts" property. */
536678453a8Sspeer 	if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0,
537678453a8Sspeer 	    "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) {
538678453a8Sspeer 		NXGE_ERROR_MSG((nxge, HIO_CTL,
539678453a8Sspeer 		    "==> nxge_hio_intr_init(obp): no 'interrupts' property"));
540678453a8Sspeer 		return (NXGE_ERROR);
541678453a8Sspeer 	}
542678453a8Sspeer 
543678453a8Sspeer 	/*
544678453a8Sspeer 	 * For each device assigned, the content of each interrupts
545678453a8Sspeer 	 * property is its logical device group.
546678453a8Sspeer 	 *
547678453a8Sspeer 	 * Assignment of interrupts property is in the the following
548678453a8Sspeer 	 * order:
549678453a8Sspeer 	 *
550678453a8Sspeer 	 * two receive channels
551678453a8Sspeer 	 * two transmit channels
552678453a8Sspeer 	 */
553678453a8Sspeer 	for (i = 0; i < prop_len; i++) {
554678453a8Sspeer 		hardware->ldg[i] = prop_val[i];
555678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
556678453a8Sspeer 		    "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d",
557678453a8Sspeer 		    nxge->function_num, i, hardware->ldg[i]));
558678453a8Sspeer 	}
559678453a8Sspeer 	ddi_prop_free(prop_val);
560678453a8Sspeer 
561678453a8Sspeer 	hardware->max_grpids = prop_len;
562678453a8Sspeer 	hardware->max_ldgs = prop_len;
563678453a8Sspeer 	hardware->ldg_chn_start = 0;
564678453a8Sspeer 
565678453a8Sspeer 	/* ----------------------------------------------------- */
566678453a8Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
567678453a8Sspeer 
568678453a8Sspeer 	interrupts->intr_registered = B_FALSE;
569678453a8Sspeer 	interrupts->intr_enabled = B_FALSE;
570678453a8Sspeer 	interrupts->start_inum = 0;
571678453a8Sspeer 
572678453a8Sspeer 	ddi_status = ddi_intr_get_supported_types(
573678453a8Sspeer 	    nxge->dip, &interrupts->intr_types);
574678453a8Sspeer 	if (ddi_status != DDI_SUCCESS) {
575678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
576678453a8Sspeer 		    "ddi_intr_get_supported_types() returned 0x%x, "
577678453a8Sspeer 		    "types = 0x%x", ddi_status, interrupts->intr_types));
578678453a8Sspeer 		return (NXGE_ERROR);
579678453a8Sspeer 	}
580678453a8Sspeer 
581678453a8Sspeer 	NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() "
582678453a8Sspeer 	    "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types));
583678453a8Sspeer 
584678453a8Sspeer 	/* HIOXXX hack */
585678453a8Sspeer 	interrupts->intr_type = DDI_INTR_TYPE_FIXED;
586678453a8Sspeer 	/* HIOXXX hack */
587678453a8Sspeer 
588678453a8Sspeer 	intr_type = interrupts->intr_type;
589678453a8Sspeer 
590678453a8Sspeer 	ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail);
591678453a8Sspeer 	if (ddi_status != DDI_SUCCESS) {
592678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
593678453a8Sspeer 		    "ddi_intr_get_navail() returned %s, navail: %d",
594678453a8Sspeer 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
595678453a8Sspeer 		    "DDI_INTR_NOTFOUND", navail));
596678453a8Sspeer 		return (NXGE_ERROR);
597678453a8Sspeer 	}
598678453a8Sspeer 
599678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
600678453a8Sspeer 	    "nxge_hio_intr_init: number of available interrupts: %d", navail));
601678453a8Sspeer 
602678453a8Sspeer 	ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs);
603678453a8Sspeer 	if (ddi_status != DDI_SUCCESS) {
604678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
605678453a8Sspeer 		    "ddi_intr_get_nintrs() returned %s, nintrs: %d",
606678453a8Sspeer 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
607678453a8Sspeer 		    "DDI_INTR_NOTFOUND", nintrs));
608678453a8Sspeer 		return (NXGE_ERROR);
609678453a8Sspeer 	}
610678453a8Sspeer 
611678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
612678453a8Sspeer 	    "nxge_hio_intr_init: number of interrupts: %d", nintrs));
613678453a8Sspeer 
614678453a8Sspeer 	interrupts->intr_size = navail * sizeof (ddi_intr_handle_t);
615678453a8Sspeer 	interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP);
616678453a8Sspeer 
617678453a8Sspeer 	/*
618678453a8Sspeer 	 * When <behavior> is set to  DDI_INTR_ALLOC_STRICT,
619678453a8Sspeer 	 * ddi_intr_alloc() succeeds if and only if <navail>
620678453a8Sspeer 	 * interrupts are are allocated. Otherwise, it fails.
621678453a8Sspeer 	 */
622678453a8Sspeer 	behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ?
623678453a8Sspeer 	    DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL);
624678453a8Sspeer 
625678453a8Sspeer 	ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type,
626678453a8Sspeer 	    inum, navail, &nactual, behavior);
627678453a8Sspeer 	if (ddi_status != DDI_SUCCESS) {
628678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
629678453a8Sspeer 		    "ddi_intr_alloc() returned 0x%x%, "
630678453a8Sspeer 		    "number allocated: %d", ddi_status, nactual));
631678453a8Sspeer 		return (NXGE_ERROR);
632678453a8Sspeer 	}
633678453a8Sspeer 
634678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
635678453a8Sspeer 	    "nxge_hio_intr_init: number of interrupts allocated: %d", nactual));
636678453a8Sspeer 
637678453a8Sspeer 	/* <ninterrupts> is a dead variable: we may as well use it. */
638678453a8Sspeer 	hardware->ninterrupts = nactual;
639678453a8Sspeer 
640678453a8Sspeer 	/* FOI: Get the interrupt priority. */
641678453a8Sspeer 	if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0],
642678453a8Sspeer 	    (uint_t *)&interrupts->pri)) != DDI_SUCCESS) {
643678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
644678453a8Sspeer 		    " ddi_intr_get_pri() failed: %d", ddi_status));
645678453a8Sspeer 	}
646678453a8Sspeer 
647678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
648678453a8Sspeer 	    "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri));
649678453a8Sspeer 
650678453a8Sspeer 	/* FOI: Get our interrupt capability flags. */
651678453a8Sspeer 	if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0],
652678453a8Sspeer 	    &interrupts->intr_cap)) != DDI_SUCCESS) {
653678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
654678453a8Sspeer 		    "ddi_intr_get_cap() failed: %d", ddi_status));
655678453a8Sspeer 	}
656678453a8Sspeer 
657678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
658678453a8Sspeer 	    "nxge_hio_intr_init: interrupt capabilities: %d",
659678453a8Sspeer 	    interrupts->intr_cap));
660678453a8Sspeer 
661678453a8Sspeer 	interrupts->intr_registered = B_TRUE;
662678453a8Sspeer 
663678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init"));
664678453a8Sspeer 
665678453a8Sspeer 	return (NXGE_OK);
666678453a8Sspeer }
667678453a8Sspeer 
668678453a8Sspeer /*
669678453a8Sspeer  * nxge_hio_intr_uninit
670678453a8Sspeer  *
671678453a8Sspeer  *	Uninitialize interrupts in a guest domain.
672678453a8Sspeer  *
673678453a8Sspeer  * Arguments:
674678453a8Sspeer  * 	nxge
675678453a8Sspeer  *
676678453a8Sspeer  * Notes:
677678453a8Sspeer  *
678678453a8Sspeer  * Context:
679678453a8Sspeer  *	Guest domain
680678453a8Sspeer  */
681678453a8Sspeer void
682678453a8Sspeer nxge_hio_intr_uninit(
683678453a8Sspeer 	nxge_t *nxge)
684678453a8Sspeer {
685678453a8Sspeer 	nxge_hw_pt_cfg_t *hardware;
686678453a8Sspeer 	nxge_intr_t *interrupts;
687678453a8Sspeer 	nxge_ldgv_t *control;
688678453a8Sspeer 	int i;
689678453a8Sspeer 
690678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit"));
691678453a8Sspeer 
692678453a8Sspeer 	/* ----------------------------------------------------- */
693678453a8Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
694678453a8Sspeer 
695678453a8Sspeer 	/*
696678453a8Sspeer 	 * If necessary, disable any currently active interrupts.
697678453a8Sspeer 	 */
698678453a8Sspeer 	if (interrupts->intr_enabled) {
699678453a8Sspeer 		nxge_grp_set_t *set;
700678453a8Sspeer 		nxge_grp_t *group;
701678453a8Sspeer 		int channel;
702678453a8Sspeer 
703678453a8Sspeer 		set = &nxge->tx_set;
704678453a8Sspeer 		group = set->group[0];	/* Assumption: only one group! */
705678453a8Sspeer 		for (channel = 0; channel < NXGE_MAX_TDCS; channel++) {
706678453a8Sspeer 			if ((1 << channel) & group->map) {
707678453a8Sspeer 				(void) nxge_hio_intr_remove(
708678453a8Sspeer 				    nxge, VP_BOUND_TX, channel);
709678453a8Sspeer 			}
710678453a8Sspeer 		}
711678453a8Sspeer 
712678453a8Sspeer 		set = &nxge->rx_set;
713678453a8Sspeer 		group = set->group[0];	/* Assumption: only one group! */
714678453a8Sspeer 		for (channel = 0; channel < NXGE_MAX_RDCS; channel++) {
715678453a8Sspeer 			if ((1 << channel) & group->map) {
716678453a8Sspeer 				(void) nxge_hio_intr_remove(
717678453a8Sspeer 				    nxge, VP_BOUND_RX, channel);
718678453a8Sspeer 			}
719678453a8Sspeer 		}
720678453a8Sspeer 	}
721678453a8Sspeer 
722678453a8Sspeer 	/*
723678453a8Sspeer 	 * Free all of our allocated interrupts.
724678453a8Sspeer 	 */
725678453a8Sspeer 	hardware = &nxge->pt_config.hw_config;
726678453a8Sspeer 	for (i = 0; i < hardware->ninterrupts; i++) {
727678453a8Sspeer 		if (interrupts->htable[i])
728678453a8Sspeer 			(void) ddi_intr_free(interrupts->htable[i]);
729678453a8Sspeer 		interrupts->htable[i] = 0;
730678453a8Sspeer 	}
731678453a8Sspeer 
732678453a8Sspeer 	interrupts->intr_registered = B_FALSE;
73348056c53SMichael Speer 	KMEM_FREE(interrupts->htable, interrupts->intr_size);
73448056c53SMichael Speer 	interrupts->htable = NULL;
735678453a8Sspeer 
7369d5b8bc5SMichael Speer 	if (nxge->ldgvp == NULL)
7379d5b8bc5SMichael Speer 		goto nxge_hio_intr_uninit_exit;
7389d5b8bc5SMichael Speer 
739678453a8Sspeer 	control = nxge->ldgvp;
740678453a8Sspeer 	if (control->ldgp) {
741678453a8Sspeer 		KMEM_FREE(control->ldgp,
742678453a8Sspeer 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
743678453a8Sspeer 		control->ldgp = 0;
744678453a8Sspeer 	}
745678453a8Sspeer 
746678453a8Sspeer 	if (control->ldvp) {
747678453a8Sspeer 		KMEM_FREE(control->ldvp,
748678453a8Sspeer 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
749678453a8Sspeer 		control->ldvp = 0;
750678453a8Sspeer 	}
751678453a8Sspeer 
75248056c53SMichael Speer 	KMEM_FREE(control, sizeof (nxge_ldgv_t));
75348056c53SMichael Speer 	nxge->ldgvp = NULL;
75448056c53SMichael Speer 
7559d5b8bc5SMichael Speer nxge_hio_intr_uninit_exit:
756678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit"));
757678453a8Sspeer }
758678453a8Sspeer 
759678453a8Sspeer /*
760678453a8Sspeer  * nxge_hio_tdsv_add
761678453a8Sspeer  *
762678453a8Sspeer  *	Add a transmit device interrupt.
763678453a8Sspeer  *
764678453a8Sspeer  * Arguments:
765678453a8Sspeer  * 	nxge
766678453a8Sspeer  * 	dc	The TDC whose interrupt we're adding
767678453a8Sspeer  *
768678453a8Sspeer  * Notes:
769678453a8Sspeer  *
770678453a8Sspeer  * Context:
771678453a8Sspeer  *	Guest domain
772678453a8Sspeer  */
773678453a8Sspeer static
774678453a8Sspeer hv_rv_t
775678453a8Sspeer nxge_hio_tdsv_add(
776678453a8Sspeer 	nxge_t *nxge,
777678453a8Sspeer 	nxge_hio_dc_t *dc)
778678453a8Sspeer {
779678453a8Sspeer 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
780678453a8Sspeer 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
781678453a8Sspeer 	nxhv_dc_fp_t *tx = &nhd->hio.tx;
782678453a8Sspeer 	hv_rv_t hv_rv;
783678453a8Sspeer 
784678453a8Sspeer 	if (tx->getinfo == 0) {
785678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
786678453a8Sspeer 		    "nx_hio_tdsv_add: tx->getinfo absent"));
787678453a8Sspeer 		return (EINVAL);
788678453a8Sspeer 	}
789678453a8Sspeer 
790678453a8Sspeer 	/*
791678453a8Sspeer 	 * Get the dma channel information.
792678453a8Sspeer 	 */
793678453a8Sspeer 	hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
794678453a8Sspeer 	    &dc->ldg.ldsv);
795678453a8Sspeer 	if (hv_rv != 0) {
796678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
797678453a8Sspeer 		    "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
798678453a8Sspeer 		return (EIO);
799678453a8Sspeer 	}
800678453a8Sspeer 
801678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
802678453a8Sspeer 	    "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
803678453a8Sspeer 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
804678453a8Sspeer 
805678453a8Sspeer 	if (hardware->tdc.count == 0) {
806678453a8Sspeer 		hardware->tdc.start = dc->channel;
807678453a8Sspeer 	}
808678453a8Sspeer 
809678453a8Sspeer 	hardware->tdc.count++;
810678453a8Sspeer 	hardware->tdc.owned++;
811678453a8Sspeer 
812678453a8Sspeer 	/*
813678453a8Sspeer 	 * In version 1.0 of the hybrid I/O driver, there
814678453a8Sspeer 	 * are eight interrupt vectors per VR.
815678453a8Sspeer 	 *
816678453a8Sspeer 	 * Vectors 0 - 3 are reserved for RDCs.
817678453a8Sspeer 	 * Vectors 4 - 7 are reserved for TDCs.
818678453a8Sspeer 	 */
819678453a8Sspeer 	dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE;
820678453a8Sspeer 	// Version 1.0 hack only!
821678453a8Sspeer 
822678453a8Sspeer 	return (0);
823678453a8Sspeer }
824678453a8Sspeer 
825678453a8Sspeer /*
826678453a8Sspeer  * nxge_hio_rdsv_add
827678453a8Sspeer  *
828678453a8Sspeer  *	Add a transmit device interrupt.
829678453a8Sspeer  *
830678453a8Sspeer  * Arguments:
831678453a8Sspeer  * 	nxge
832678453a8Sspeer  * 	dc	The RDC whose interrupt we're adding
833678453a8Sspeer  *
834678453a8Sspeer  * Notes:
835678453a8Sspeer  *
836678453a8Sspeer  * Context:
837678453a8Sspeer  *	Guest domain
838678453a8Sspeer  */
839678453a8Sspeer static
840678453a8Sspeer hv_rv_t
841678453a8Sspeer nxge_hio_rdsv_add(
842678453a8Sspeer 	nxge_t *nxge,
843678453a8Sspeer 	nxge_hio_dc_t *dc)
844678453a8Sspeer {
845678453a8Sspeer 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
846678453a8Sspeer 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
847678453a8Sspeer 	nxhv_dc_fp_t *rx = &nhd->hio.rx;
848678453a8Sspeer 	hv_rv_t hv_rv;
849678453a8Sspeer 
850678453a8Sspeer 	if (rx->getinfo == 0) {
851678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
852678453a8Sspeer 		    "nx_hio_tdsv_add: rx->getinfo absent"));
853678453a8Sspeer 		return (EINVAL);
854678453a8Sspeer 	}
855678453a8Sspeer 
856678453a8Sspeer 	/*
857678453a8Sspeer 	 * Get DMA channel information.
858678453a8Sspeer 	 */
859678453a8Sspeer 	hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
860678453a8Sspeer 	    &dc->ldg.ldsv);
861678453a8Sspeer 	if (hv_rv != 0) {
862678453a8Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
863678453a8Sspeer 		    "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
864678453a8Sspeer 		return (EIO);
865678453a8Sspeer 	}
866678453a8Sspeer 
867678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
868678453a8Sspeer 	    "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
869678453a8Sspeer 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
870678453a8Sspeer 
871678453a8Sspeer 	if (hardware->max_rdcs == 0) {
872678453a8Sspeer 		hardware->start_rdc = dc->channel;
873678453a8Sspeer 		hardware->def_rdc = dc->channel;
874678453a8Sspeer 	}
875678453a8Sspeer 
876678453a8Sspeer 	hardware->max_rdcs++;
877678453a8Sspeer 
878678453a8Sspeer 	/*
879678453a8Sspeer 	 * In version 1.0 of the hybrid I/O driver, there
880678453a8Sspeer 	 * are eight interrupt vectors per VR.
881678453a8Sspeer 	 *
882678453a8Sspeer 	 * Vectors 0 - 3 are reserved for RDCs.
883678453a8Sspeer 	 */
884678453a8Sspeer 	dc->ldg.vector = (dc->ldg.ldsv % 2);
885678453a8Sspeer 	// Version 1.0 hack only!
886678453a8Sspeer 
887678453a8Sspeer 	return (0);
888678453a8Sspeer }
889678453a8Sspeer 
890678453a8Sspeer /*
891678453a8Sspeer  * nxge_hio_ldsv_add
892678453a8Sspeer  *
893678453a8Sspeer  *	Add a transmit or receive interrupt.
894678453a8Sspeer  *
895678453a8Sspeer  * Arguments:
896678453a8Sspeer  * 	nxge
897678453a8Sspeer  * 	dc	The DMA channel whose interrupt we're adding
898678453a8Sspeer  *
899678453a8Sspeer  * Notes:
900678453a8Sspeer  *	Guest domains can only add interrupts for DMA channels.
901678453a8Sspeer  *	They cannot access the MAC, MIF, or SYSERR interrupts.
902678453a8Sspeer  *
903678453a8Sspeer  * Context:
904678453a8Sspeer  *	Guest domain
905678453a8Sspeer  */
906*0dc2366fSVenugopal Iyer int
907*0dc2366fSVenugopal Iyer nxge_hio_ldsv_add(nxge_t *nxge, nxge_hio_dc_t *dc)
908678453a8Sspeer {
909678453a8Sspeer 	nxge_ldgv_t *control;
910678453a8Sspeer 	nxge_ldg_t *group;
911678453a8Sspeer 	nxge_ldv_t *device;
912678453a8Sspeer 
913678453a8Sspeer 	if (dc->type == VP_BOUND_TX) {
914678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)",
915678453a8Sspeer 		    dc->channel));
916*0dc2366fSVenugopal Iyer 		if (nxge_hio_tdsv_add(nxge, dc) != 0)
917*0dc2366fSVenugopal Iyer 			return (EIO);
918678453a8Sspeer 	} else {
919678453a8Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)",
920678453a8Sspeer 		    dc->channel));
921*0dc2366fSVenugopal Iyer 		if (nxge_hio_rdsv_add(nxge, dc) != 0)
922*0dc2366fSVenugopal Iyer 			return (EIO);
923678453a8Sspeer 	}
924678453a8Sspeer 
925678453a8Sspeer 	dc->ldg.map |= (1 << dc->ldg.ldsv);
926678453a8Sspeer 
927678453a8Sspeer 	control = nxge->ldgvp;
928678453a8Sspeer 	if (control == NULL) {
929678453a8Sspeer 		control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP);
930678453a8Sspeer 		nxge->ldgvp = control;
931678453a8Sspeer 		control->maxldgs = 1;
932678453a8Sspeer 		control->maxldvs = 1;
933678453a8Sspeer 		control->ldgp = KMEM_ZALLOC(
934678453a8Sspeer 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP);
935678453a8Sspeer 		control->ldvp = KMEM_ZALLOC(
936678453a8Sspeer 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP);
937678453a8Sspeer 	} else {
938678453a8Sspeer 		control->maxldgs++;
939678453a8Sspeer 		control->maxldvs++;
940678453a8Sspeer 	}
941678453a8Sspeer 
942678453a8Sspeer 	/*
943678453a8Sspeer 	 * Initialize the logical device group data structure first.
944678453a8Sspeer 	 */
945678453a8Sspeer 	group = &control->ldgp[dc->ldg.vector];
946678453a8Sspeer 
947678453a8Sspeer 	(void) memset(group, 0, sizeof (*group));
948678453a8Sspeer 
949678453a8Sspeer 	/*
950678453a8Sspeer 	 * <hw_config.ldg> is a copy of the "interrupts" property.
951678453a8Sspeer 	 */
952678453a8Sspeer 	group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector];
953678453a8Sspeer 	group->vldg_index = (uint8_t)dc->ldg.index;
954678453a8Sspeer 	/*
955678453a8Sspeer 	 * Since <vldg_index> is a dead variable, I'm reusing
956678453a8Sspeer 	 * it in Hybrid I/O to calculate the offset into the
957678453a8Sspeer 	 * virtual PIO_LDSV space.
958678453a8Sspeer 	 */
959678453a8Sspeer 
960678453a8Sspeer 	group->arm = B_TRUE;
961678453a8Sspeer 	group->ldg_timer = NXGE_TIMER_LDG;
962678453a8Sspeer 	group->func = nxge->function_num;
963678453a8Sspeer 	group->vector = dc->ldg.vector;
964678453a8Sspeer 	/*
965678453a8Sspeer 	 * <intdata> appears to be a dead variable.
966678453a8Sspeer 	 * Though it is not used anywhere in the driver,
967678453a8Sspeer 	 * we'll set it anyway.
968678453a8Sspeer 	 */
969678453a8Sspeer 	group->intdata = SID_DATA(group->func, group->vector);
970678453a8Sspeer 
971678453a8Sspeer 	group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */
972678453a8Sspeer 	group->nxgep = nxge;
973678453a8Sspeer 
974678453a8Sspeer 	/*
975678453a8Sspeer 	 * Initialize the logical device state vector next.
976678453a8Sspeer 	 */
977678453a8Sspeer 	device = &control->ldvp[dc->ldg.ldsv];
978678453a8Sspeer 
979678453a8Sspeer 	device->ldg_assigned = group->ldg;
980678453a8Sspeer 	device->ldv = dc->ldg.ldsv;
981678453a8Sspeer 
982678453a8Sspeer 	if (dc->type == VP_BOUND_TX) {
983678453a8Sspeer 		device->is_txdma = B_TRUE;
984678453a8Sspeer 		device->is_rxdma = B_FALSE;
985678453a8Sspeer 		device->ldv_intr_handler = nxge_tx_intr;
986678453a8Sspeer 	} else {
987678453a8Sspeer 		device->is_rxdma = B_TRUE;
988678453a8Sspeer 		device->is_txdma = B_FALSE;
989678453a8Sspeer 		device->ldv_intr_handler = nxge_rx_intr;
990678453a8Sspeer 	}
991678453a8Sspeer 	device->is_mif = B_FALSE;
992678453a8Sspeer 	device->is_mac = B_FALSE;
993678453a8Sspeer 	device->is_syserr = B_FALSE;
994678453a8Sspeer 	device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */
995678453a8Sspeer 
996678453a8Sspeer 	device->channel = dc->channel;
997678453a8Sspeer 	device->vdma_index = dc->page;
998678453a8Sspeer 	device->func = nxge->function_num;
999678453a8Sspeer 	device->ldgp = group;
1000678453a8Sspeer 	device->ldv_flags = 0;
1001678453a8Sspeer 	device->ldv_ldf_masks = 0;
1002678453a8Sspeer 
1003678453a8Sspeer 	device->nxgep = nxge;
1004678453a8Sspeer 
1005678453a8Sspeer 	/*
1006678453a8Sspeer 	 * This code seems to imply a strict 1-to-1 correspondence.
1007678453a8Sspeer 	 */
1008678453a8Sspeer 	group->nldvs++;
1009678453a8Sspeer 	group->ldvp = device;
1010678453a8Sspeer 
1011678453a8Sspeer 	control->nldvs++;
1012678453a8Sspeer 	control->ldg_intrs++;
1013678453a8Sspeer 
1014678453a8Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add"));
1015678453a8Sspeer 
1016678453a8Sspeer 	return (0);
1017678453a8Sspeer }
1018678453a8Sspeer 
1019678453a8Sspeer /*
1020678453a8Sspeer  * nxge_hio_ldsv_im
1021678453a8Sspeer  *
1022678453a8Sspeer  *	Manage a VLDG's interrupts.
1023678453a8Sspeer  *
1024678453a8Sspeer  * Arguments:
1025678453a8Sspeer  * 	nxge
1026678453a8Sspeer  * 	group	The VLDG to manage
1027678453a8Sspeer  *
1028678453a8Sspeer  * Notes:
1029678453a8Sspeer  *	There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
1030678453a8Sspeer  *	That sums to 256 bytes of virtual PIO_LDSV space.
1031678453a8Sspeer  *
1032678453a8Sspeer  *		VLDG0 starts at offset 0,
1033678453a8Sspeer  *		VLDG1 starts at offset 32, etc.
1034678453a8Sspeer  *
1035678453a8Sspeer  *	Each set consists of 4 registers:
1036678453a8Sspeer  *		Logical Device State Vector 0. LDSV0
1037678453a8Sspeer  *		Logical Device State Vector 1. LDSV1
1038678453a8Sspeer  *		Logical Device State Vector 2. LDSV2
1039678453a8Sspeer  *		Logical Device Group Interrupt Management. LDGIMGN
1040678453a8Sspeer  *
1041678453a8Sspeer  *	The first three (LDSVx) are read-only.  The 4th register is the
1042678453a8Sspeer  *	LDGIMGN, the LDG Interrupt Management register, which is used to
1043678453a8Sspeer  *	arm the LDG, or set its timer.
1044678453a8Sspeer  *
1045678453a8Sspeer  *	The offset to write to is calculated as follows:
1046678453a8Sspeer  *
1047678453a8Sspeer  *		0x2000 + (VLDG << 4) + offset, where:
1048678453a8Sspeer  *		VDLG is the virtual group, i.e., index of the LDG.
1049678453a8Sspeer  *		offset is the offset (alignment 8) of the register
1050678453a8Sspeer  *		       to read or write.
1051678453a8Sspeer  *
1052678453a8Sspeer  *	So, for example, if we wanted to arm the first TDC of VRx, we would
1053678453a8Sspeer  *	calculate the address as:
1054678453a8Sspeer  *
1055678453a8Sspeer  *	0x2000 + (0 << 4) + 0x18 = 0x18
1056678453a8Sspeer  *
1057678453a8Sspeer  * Context:
1058678453a8Sspeer  *	Guest domain
1059678453a8Sspeer  *
1060678453a8Sspeer  */
1061678453a8Sspeer void
1062678453a8Sspeer nxge_hio_ldsv_im(
1063678453a8Sspeer 	/* Read any register in the PIO_LDSV space. */
1064678453a8Sspeer 	nxge_t *nxge,
1065678453a8Sspeer 	nxge_ldg_t *group,
1066678453a8Sspeer 	pio_ld_op_t op,
1067678453a8Sspeer 	uint64_t *value)
1068678453a8Sspeer {
1069678453a8Sspeer 	uint64_t offset = VLDG_OFFSET;
1070678453a8Sspeer 
1071678453a8Sspeer 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1072678453a8Sspeer 	offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */
1073678453a8Sspeer 
1074678453a8Sspeer 	NXGE_REG_RD64(nxge->npi_handle, offset, value);
1075678453a8Sspeer }
1076678453a8Sspeer 
1077678453a8Sspeer void
1078678453a8Sspeer nxge_hio_ldgimgn(
1079678453a8Sspeer 	/* Write the PIO_LDGIMGN register. */
1080678453a8Sspeer 	nxge_t *nxge,
1081678453a8Sspeer 	nxge_ldg_t *group)
1082678453a8Sspeer {
1083678453a8Sspeer 	uint64_t offset = VLDG_OFFSET;
1084678453a8Sspeer 	ldgimgm_t mgm;
1085678453a8Sspeer 
1086678453a8Sspeer 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
1087678453a8Sspeer 	offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */
1088678453a8Sspeer 
1089678453a8Sspeer 	mgm.value = 0;
1090678453a8Sspeer 	if (group->arm) {
1091678453a8Sspeer 		mgm.bits.ldw.arm = 1;
1092678453a8Sspeer 		mgm.bits.ldw.timer = group->ldg_timer;
1093678453a8Sspeer 	} else {
1094678453a8Sspeer 		mgm.bits.ldw.arm = 0;
1095678453a8Sspeer 		mgm.bits.ldw.timer = 0;
1096678453a8Sspeer 	}
1097678453a8Sspeer 	NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value);
1098678453a8Sspeer }
1099