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