xref: /onnv-gate/usr/src/uts/common/io/nxge/nxge_intr.c (revision 9232:36814323355d)
16495Sspeer /*
26495Sspeer  * CDDL HEADER START
36495Sspeer  *
46495Sspeer  * The contents of this file are subject to the terms of the
56495Sspeer  * Common Development and Distribution License (the "License").
66495Sspeer  * You may not use this file except in compliance with the License.
76495Sspeer  *
86495Sspeer  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96495Sspeer  * or http://www.opensolaris.org/os/licensing.
106495Sspeer  * See the License for the specific language governing permissions
116495Sspeer  * and limitations under the License.
126495Sspeer  *
136495Sspeer  * When distributing Covered Code, include this CDDL HEADER in each
146495Sspeer  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156495Sspeer  * If applicable, add the following below this CDDL HEADER, with the
166495Sspeer  * fields enclosed by brackets "[]" replaced with your own identifying
176495Sspeer  * information: Portions Copyright [yyyy] [name of copyright owner]
186495Sspeer  *
196495Sspeer  * CDDL HEADER END
206495Sspeer  */
216495Sspeer 
226495Sspeer /*
23*9232SMichael.Speer@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
246495Sspeer  * Use is subject to license terms.
256495Sspeer  */
266495Sspeer 
276495Sspeer /*
286495Sspeer  * nxge_intr.c
296495Sspeer  *
306495Sspeer  * This file manages the interrupts for a hybrid I/O (hio) device.
316495Sspeer  * In the future, it may manage interrupts for all Neptune-based
326495Sspeer  * devices.
336495Sspeer  *
346495Sspeer  */
356495Sspeer 
366495Sspeer #include <sys/nxge/nxge_impl.h>
376495Sspeer #include <sys/nxge/nxge_hio.h>
386495Sspeer 
396495Sspeer /*
406495Sspeer  * External prototypes
416495Sspeer  */
426495Sspeer 
436495Sspeer /* The following function may be found in nxge_[t|r]xdma.c */
446495Sspeer extern uint_t nxge_tx_intr(void *, void *);
456495Sspeer extern uint_t nxge_rx_intr(void *, void *);
466495Sspeer 
476495Sspeer /*
486495Sspeer  * Local prototypes
496495Sspeer  */
506495Sspeer static int nxge_intr_vec_find(nxge_t *, vpc_type_t, int);
516495Sspeer 
526495Sspeer /*
536495Sspeer  * nxge_intr_add
546495Sspeer  *
556495Sspeer  *	Add <channel>'s interrupt.
566495Sspeer  *
576495Sspeer  * Arguments:
586495Sspeer  * 	nxge
596495Sspeer  * 	type	Tx or Rx
606495Sspeer  * 	channel	The channel whose interrupt we want to add.
616495Sspeer  *
626495Sspeer  * Notes:
636495Sspeer  *	Add here means: add a handler, enable, & arm the interrupt.
646495Sspeer  *
656495Sspeer  * Context:
666495Sspeer  *	Service domain
676495Sspeer  *
686495Sspeer  */
696495Sspeer nxge_status_t
706495Sspeer nxge_intr_add(
716495Sspeer 	nxge_t *nxge,
726495Sspeer 	vpc_type_t type,
736495Sspeer 	int channel)
746495Sspeer {
756495Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
766495Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
776495Sspeer 	nxge_ldv_t	*ldvp;
786495Sspeer 
796495Sspeer 	uint_t		*inthandler; /* A parameter to ddi_intr_add_handler */
806495Sspeer 	int		vector;
816495Sspeer 	int		status1, status2;
826495Sspeer 
836495Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
846495Sspeer 
856495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_add"));
866495Sspeer 
876495Sspeer 	if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
886495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
896495Sspeer 		    "nxge_intr_add(%cDC %d): vector not found", c, channel));
906495Sspeer 		return (NXGE_ERROR);
916495Sspeer 	}
926495Sspeer 
936495Sspeer 	ldvp = &nxge->ldgvp->ldvp[vector];
946495Sspeer 	group = ldvp->ldgp;
956495Sspeer 
966495Sspeer 	if (group->nldvs == 1) {
976495Sspeer 		inthandler = (uint_t *)group->ldvp->ldv_intr_handler;
986495Sspeer 	} else if (group->nldvs > 1) {
996495Sspeer 		inthandler = (uint_t *)group->sys_intr_handler;
1006495Sspeer 	}
1016495Sspeer 
1026495Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
1036495Sspeer 
1046495Sspeer 	status1 = DDI_SUCCESS;
1056495Sspeer 
1066495Sspeer 	if ((status2 = ddi_intr_add_handler(interrupts->htable[vector],
1076495Sspeer 	    (ddi_intr_handler_t *)inthandler, group->ldvp, nxge))
1086495Sspeer 	    != DDI_SUCCESS) {
1096495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
1106495Sspeer 		    "ddi_intr_add_handler(%d) returned %s",
1116495Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
1126495Sspeer 		status1 += status2;
1136495Sspeer 	}
1146495Sspeer 
1156495Sspeer 	interrupts->intr_added++;
1166495Sspeer 
1176495Sspeer 	/* Enable the interrupt. */
1186495Sspeer 	if ((status2 = ddi_intr_enable(interrupts->htable[vector]))
1196495Sspeer 	    != DDI_SUCCESS) {
1206495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): "
1216495Sspeer 		    "ddi_intr_enable(%d) returned %s",
1226495Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
1236495Sspeer 		status1 += status2;
1246495Sspeer 	}
1256495Sspeer 
1266495Sspeer 	if (status1 == DDI_SUCCESS) {
1276495Sspeer 		interrupts->intr_enabled = B_TRUE;
1286495Sspeer 
1296495Sspeer 		/* Finally, arm the interrupt. */
1306495Sspeer 		if (group->nldvs == 1) {
1316495Sspeer 			npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
1326495Sspeer 			(void) npi_intr_ldg_mgmt_set(handle, group->ldg,
1336495Sspeer 			    B_TRUE, group->ldg_timer);
1346495Sspeer 		}
1356495Sspeer 	}
1366495Sspeer 
1376495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_add"));
1386495Sspeer 
1396495Sspeer 	return (NXGE_OK);
1406495Sspeer }
1416495Sspeer 
1426495Sspeer /*
1436495Sspeer  * nxge_intr_remove
1446495Sspeer  *
1456495Sspeer  *	Remove <channel>'s interrupt.
1466495Sspeer  *
1476495Sspeer  * Arguments:
1486495Sspeer  * 	nxge
1496495Sspeer  * 	type	Tx or Rx
1506495Sspeer  * 	channel	The channel whose interrupt we want to remove.
1516495Sspeer  *
1526495Sspeer  * Notes:
1536495Sspeer  *	Remove here means: disarm, disable, & remove the handler.
1546495Sspeer  *
1556495Sspeer  * Context:
1566495Sspeer  *	Service domain
1576495Sspeer  *
1586495Sspeer  */
1596495Sspeer nxge_status_t
1606495Sspeer nxge_intr_remove(
1616495Sspeer 	nxge_t *nxge,
1626495Sspeer 	vpc_type_t type,
1636495Sspeer 	int channel)
1646495Sspeer {
1656495Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
1666495Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
1676495Sspeer 	nxge_ldv_t	*ldvp;
1686495Sspeer 
1696495Sspeer 	int		vector;
1706495Sspeer 	int		status1, status2;
1716495Sspeer 
1726495Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
1736495Sspeer 
1746495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_remove"));
1756495Sspeer 
1766495Sspeer 	if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) {
1776495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
1786495Sspeer 		    "nxge_intr_remove(%cDC %d): vector not found", c, channel));
1796495Sspeer 		return (NXGE_ERROR);
1806495Sspeer 	}
1816495Sspeer 
1826495Sspeer 	ldvp = &nxge->ldgvp->ldvp[vector];
1836495Sspeer 	group = ldvp->ldgp;
1846495Sspeer 
1856495Sspeer 	/* Disarm the interrupt. */
1866495Sspeer 	if (group->nldvs == 1) {
1876495Sspeer 		npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge);
1886495Sspeer 		group->arm = B_FALSE;
1896495Sspeer 		(void) npi_intr_ldg_mgmt_set(handle, group->ldg,
1906495Sspeer 		    B_TRUE, group->ldg_timer);
1916495Sspeer 		group->arm = B_TRUE; /* HIOXXX There IS a better way */
1926495Sspeer 	}
1936495Sspeer 
1946495Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
1956495Sspeer 
1966495Sspeer 	status1 = DDI_SUCCESS;
1976495Sspeer 
1986495Sspeer 	/* Disable the interrupt. */
1996495Sspeer 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
2006495Sspeer 	    != DDI_SUCCESS) {
2016495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
2026495Sspeer 		    ": ddi_intr_disable(%d) returned %s",
2036495Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
2046495Sspeer 		status1 += status2;
2056495Sspeer 	}
2066495Sspeer 
2076495Sspeer 	/* Remove the interrupt handler. */
2086495Sspeer 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
2096495Sspeer 	    != DDI_SUCCESS) {
2106495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)"
2116495Sspeer 		    ": ddi_intr_remove_handler(%d) returned %s",
2126495Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
2136495Sspeer 		status1 += status2;
2146495Sspeer 	}
2156495Sspeer 
2166495Sspeer 	if (status1 == DDI_SUCCESS) {
2176495Sspeer 		interrupts->intr_added--;
2186495Sspeer 		if (interrupts->intr_added == 0)
2196495Sspeer 			interrupts->intr_enabled = B_FALSE;
2206495Sspeer 	}
2216495Sspeer 
2226495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_remove"));
2236495Sspeer 
2246495Sspeer 	return (NXGE_OK);
2256495Sspeer }
2266495Sspeer 
2276495Sspeer /*
2286495Sspeer  * nxge_intr_vec_find
2296495Sspeer  *
2306495Sspeer  *	Find the interrupt vector associated with <channel>.
2316495Sspeer  *
2326495Sspeer  * Arguments:
2336495Sspeer  * 	nxge
2346495Sspeer  * 	type	Tx or Rx
2356495Sspeer  * 	channel	The channel whose vector we want to find.
2366495Sspeer  *
2376495Sspeer  * Notes:
2386495Sspeer  *
2396495Sspeer  * Context:
2406495Sspeer  *	Service domain
2416495Sspeer  *
2426495Sspeer  */
2436495Sspeer static
2446495Sspeer int
2456495Sspeer nxge_intr_vec_find(
2466495Sspeer 	nxge_t *nxge,
2476495Sspeer 	vpc_type_t type,
2486495Sspeer 	int channel)
2496495Sspeer {
2506495Sspeer 	nxge_hw_pt_cfg_t *hardware;
2516495Sspeer 	nxge_ldgv_t	*ldgvp;
2526495Sspeer 	nxge_ldv_t	*ldvp;
2536495Sspeer 
2546495Sspeer 	int		first, limit, vector;
2556495Sspeer 
2566495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
2576495Sspeer 	    "==> nxge_intr_vec_find(%cDC %d)",
2586495Sspeer 	    type == VP_BOUND_TX ? 'T' : 'R', channel));
2596495Sspeer 
2606495Sspeer 	if (nxge->ldgvp == 0) {
2616495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
2626495Sspeer 		    "nxge_hio_intr_vec_find(%cDC %d): ldgvp == 0",
2636495Sspeer 		    type == VP_BOUND_TX ? 'T' : 'R', channel));
2646495Sspeer 		return (-1);
2656495Sspeer 	}
2666495Sspeer 
2676495Sspeer 	hardware = &nxge->pt_config.hw_config;
2686495Sspeer 
2696495Sspeer 	first = hardware->ldg_chn_start;
2706495Sspeer 	if (type == VP_BOUND_TX) {
2716495Sspeer 		first += 8;	/* HIOXXX N2/NIU hack */
2726495Sspeer 		limit = first + hardware->tdc.count;
2736495Sspeer 	} else {
2746495Sspeer 		limit = first + hardware->max_rdcs;
2756495Sspeer 	}
2766495Sspeer 
2776495Sspeer 	ldgvp = nxge->ldgvp;
2786495Sspeer 	for (vector = first; vector < limit; vector++) {
2796495Sspeer 		ldvp = &ldgvp->ldvp[vector];
2806495Sspeer 		if (ldvp->channel == channel)
2816495Sspeer 			break;
2826495Sspeer 	}
2836495Sspeer 
2846495Sspeer 	if (vector == limit) {
2856495Sspeer 		return (-1);
2866495Sspeer 	}
2876495Sspeer 
2886495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_vec_find"));
2896495Sspeer 
2906495Sspeer 	return (vector);
2916495Sspeer }
2926495Sspeer 
2936495Sspeer /*
2946495Sspeer  * ---------------------------------------------------------------------
2956495Sspeer  * HIO-specific interrupt functions.
2966495Sspeer  * ---------------------------------------------------------------------
2976495Sspeer  */
2986495Sspeer 
2996495Sspeer /*
3006495Sspeer  * nxge_hio_intr_add
3016495Sspeer  *
3026495Sspeer  *	Add <channel>'s interrupt.
3036495Sspeer  *
3046495Sspeer  * Arguments:
3056495Sspeer  * 	nxge
3066495Sspeer  * 	type	Tx or Rx
3076495Sspeer  * 	channel	The channel whose interrupt we want to remove.
3086495Sspeer  *
3096495Sspeer  * Notes:
3106495Sspeer  *
3116495Sspeer  * Context:
3126495Sspeer  *	Guest domain
3136495Sspeer  *
3146495Sspeer  */
3156495Sspeer nxge_status_t
3166495Sspeer nxge_hio_intr_add(
3176495Sspeer 	nxge_t *nxge,
3186495Sspeer 	vpc_type_t type,
3196495Sspeer 	int channel)
3206495Sspeer {
3216495Sspeer 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
3226495Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
3236495Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
3246495Sspeer 	uint_t		*inthandler; /* A parameter to ddi_intr_add_handler */
3256495Sspeer 
3266495Sspeer 	int		vector;	/* A shorthand variable */
3276495Sspeer 	int		ddi_status; /* The response to ddi_intr_add_handler */
3286495Sspeer 
3296495Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
3306495Sspeer 
3316495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
3326495Sspeer 	    "==> nxge_hio_intr_add(%cDC %d)", c, channel));
3336495Sspeer 
3346495Sspeer 	if (nxge->ldgvp == 0) {
3356495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
3366495Sspeer 		    "nxge_hio_intr_add(%cDC %d): ldgvp == 0", c, channel));
3376495Sspeer 		return (NXGE_ERROR);
3386495Sspeer 	}
3396495Sspeer 
3406495Sspeer 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
3416495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
3426495Sspeer 		    "nxge_hio_intr_add: find(%s, %d) failed", c, channel));
3436495Sspeer 		return (NXGE_ERROR);
3446495Sspeer 	}
3456495Sspeer 
3466495Sspeer 	/* 'nxge_intr_type' is a bad name for this data structure. */
3476495Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
3486495Sspeer 
3496495Sspeer 	/* Set <vector> here to make the following code easier to read. */
3506495Sspeer 	vector = dc->ldg.vector;
3516495Sspeer 
3526495Sspeer 	group = &nxge->ldgvp->ldgp[vector];
3536495Sspeer 
3546495Sspeer 	if (group->nldvs == 1) {
3556495Sspeer 		inthandler = (uint_t *)group->ldvp->ldv_intr_handler;
3566495Sspeer 	} else if (group->nldvs > 1) {
3576495Sspeer 		inthandler = (uint_t *)group->sys_intr_handler;
3586495Sspeer 	}
3596495Sspeer 
3606495Sspeer 	if ((ddi_status = ddi_intr_add_handler(interrupts->htable[vector],
3616495Sspeer 	    (ddi_intr_handler_t *)inthandler, group->ldvp, nxge))
3626495Sspeer 	    != DDI_SUCCESS) {
3636495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
3646495Sspeer 		    "nxge_hio_intr_add(%cDC %d): "
3656495Sspeer 		    "ddi_intr_add_handler(%d) returned %s",
3666495Sspeer 		    c, channel, vector, nxge_ddi_perror(ddi_status)));
3676495Sspeer 		return (NXGE_ERROR);
3686495Sspeer 	}
3696495Sspeer 
3706495Sspeer 	interrupts->intr_added++;
3716495Sspeer 
3726495Sspeer 	/* Enable the interrupt. */
3736495Sspeer 	if ((ddi_status = ddi_intr_enable(interrupts->htable[vector]))
3746495Sspeer 	    != DDI_SUCCESS) {
3756495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
3766495Sspeer 		    "nxge_hio_intr_add(%cDC %d): "
3776495Sspeer 		    "ddi_intr_enable(%d) returned %s",
3786495Sspeer 		    c, channel, vector, nxge_ddi_perror(ddi_status)));
3796495Sspeer 		return (NXGE_ERROR);
3806495Sspeer 	}
3816495Sspeer 
3826495Sspeer 	interrupts->intr_enabled = B_TRUE;
3836495Sspeer 
384*9232SMichael.Speer@Sun.COM 	/*
385*9232SMichael.Speer@Sun.COM 	 * Note: RDC interrupts will be armed in nxge_m_start(). This
386*9232SMichael.Speer@Sun.COM 	 * prevents us from getting an interrupt before we are ready
387*9232SMichael.Speer@Sun.COM 	 * to process packets.
388*9232SMichael.Speer@Sun.COM 	 */
389*9232SMichael.Speer@Sun.COM 	if (type == VP_BOUND_TX) {
390*9232SMichael.Speer@Sun.COM 		nxge_hio_ldgimgn(nxge, group);
391*9232SMichael.Speer@Sun.COM 	}
3926495Sspeer 
3936495Sspeer 	dc->interrupting = B_TRUE;
3946495Sspeer 
3956495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add"));
3966495Sspeer 
3976495Sspeer 	return (NXGE_OK);
3986495Sspeer }
3996495Sspeer 
4006495Sspeer /*
4016495Sspeer  * nxge_hio_intr_remove
4026495Sspeer  *
4036495Sspeer  *	Remove <channel>'s interrupt.
4046495Sspeer  *
4056495Sspeer  * Arguments:
4066495Sspeer  * 	nxge
4076495Sspeer  * 	type	Tx or Rx
4086495Sspeer  * 	channel	The channel whose interrupt we want to remove.
4096495Sspeer  *
4106495Sspeer  * Notes:
4116495Sspeer  *
4126495Sspeer  * Context:
4136495Sspeer  *	Guest domain
4146495Sspeer  *
4156495Sspeer  */
4166495Sspeer nxge_status_t
4176495Sspeer nxge_hio_intr_remove(
4186495Sspeer 	nxge_t *nxge,
4196495Sspeer 	vpc_type_t type,
4206495Sspeer 	int channel)
4216495Sspeer {
4226495Sspeer 	nxge_hio_dc_t	*dc;	/* The relevant DMA channel data structure. */
4236495Sspeer 	nxge_intr_t	*interrupts; /* The global interrupt data. */
4246495Sspeer 	nxge_ldg_t	*group;	/* The logical device group data. */
4256495Sspeer 
4266495Sspeer 	int		vector;	/* A shorthand variable */
4276495Sspeer 	int		status1, status2;
4286495Sspeer 
4296495Sspeer 	char c = (type == VP_BOUND_TX ? 'T' : 'R');
4306495Sspeer 
4316495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
4326495Sspeer 	    "==> nxge_hio_intr_remove(%cDC %d)", c, channel));
4336495Sspeer 
4346495Sspeer 	if (nxge->ldgvp == 0) {
4356495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
4366495Sspeer 		    "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel));
4376495Sspeer 		return (NXGE_ERROR);
4386495Sspeer 	}
4396495Sspeer 
4406495Sspeer 	if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) {
4416495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
4426495Sspeer 		    "nxge_hio_intr_remove(%cDC %d): DC FIND failed",
4436495Sspeer 		    c, channel));
4446495Sspeer 		return (NXGE_ERROR);
4456495Sspeer 	}
4466495Sspeer 
4476495Sspeer 	if (dc->interrupting == B_FALSE) {
4486495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
4496495Sspeer 		    "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE",
4506495Sspeer 		    c, channel));
4516495Sspeer 		return (NXGE_OK);
4526495Sspeer 	}
4536495Sspeer 
4546495Sspeer 	/* 'nxge_intr_type' is a bad name for this data structure. */
4556495Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
4566495Sspeer 
4576495Sspeer 	/* Set <vector> here to make the following code easier to read. */
4586495Sspeer 	vector = dc->ldg.vector;
4596495Sspeer 
4606495Sspeer 	group = &nxge->ldgvp->ldgp[vector];
4616495Sspeer 
4626495Sspeer 	/* Disarm the interrupt. */
4636495Sspeer 	group->arm = B_FALSE;
4646495Sspeer 	nxge_hio_ldgimgn(nxge, group);
4656495Sspeer 	group->arm = B_TRUE;	/* HIOXXX There IS a better way */
4666495Sspeer 
4676495Sspeer 	status1 = DDI_SUCCESS;
4686495Sspeer 
4696495Sspeer 	/* Disable the interrupt. */
4706495Sspeer 	if ((status2 = ddi_intr_disable(interrupts->htable[vector]))
4716495Sspeer 	    != DDI_SUCCESS) {
4726495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
4736495Sspeer 		    "nxge_hio_intr_remove(%cDC %d): "
4746495Sspeer 		    "ddi_intr_disable(%d) returned %s",
4756495Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
4766495Sspeer 		status1 += status2;
4776495Sspeer 	}
4786495Sspeer 
4796495Sspeer 	/* Remove the interrupt handler. */
4806495Sspeer 	if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector]))
4816495Sspeer 	    != DDI_SUCCESS) {
4826495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
4836495Sspeer 		    "nxge_hio_intr_remove(%cDC %d): "
4846495Sspeer 		    "ddi_intr_remove_handle(%d) returned %s",
4856495Sspeer 		    c, channel, vector, nxge_ddi_perror(status2)));
4866495Sspeer 		status1 += status2;
4876495Sspeer 	}
4886495Sspeer 
4896495Sspeer 	if (status1 == DDI_SUCCESS) {
4906495Sspeer 		dc->interrupting = B_FALSE;
4916495Sspeer 
4926495Sspeer 		interrupts->intr_added--;
4936495Sspeer 		if (interrupts->intr_added == 0)
4946495Sspeer 			interrupts->intr_enabled = B_FALSE;
4956495Sspeer 	}
4966495Sspeer 
4976495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove"));
4986495Sspeer 
4996495Sspeer 	return (NXGE_OK);
5006495Sspeer }
5016495Sspeer 
5026495Sspeer /*
5036495Sspeer  * nxge_hio_intr_init
5046495Sspeer  *
5056495Sspeer  *	Initialize interrupts in a guest domain.
5066495Sspeer  *
5076495Sspeer  * Arguments:
5086495Sspeer  * 	nxge
5096495Sspeer  *
5106495Sspeer  * Notes:
5116495Sspeer  *
5126495Sspeer  * Context:
5136495Sspeer  *	Guest domain
5146495Sspeer  *
5156495Sspeer  */
5166495Sspeer nxge_status_t
5176495Sspeer nxge_hio_intr_init(
5186495Sspeer 	nxge_t *nxge)
5196495Sspeer {
5206495Sspeer 	int		*prop_val;
5216495Sspeer 	uint_t		prop_len;
5226495Sspeer 
5236495Sspeer 	nxge_intr_t	*interrupts;
5246495Sspeer 
5256495Sspeer 	int		intr_type, behavior;
5266495Sspeer 	int		nintrs, navail, nactual;
5276495Sspeer 	int		inum = 0;
5286495Sspeer 	int		ddi_status = DDI_SUCCESS;
5296495Sspeer 
5306495Sspeer 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
5316495Sspeer 	int i;
5326495Sspeer 
5336495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init"));
5346495Sspeer 
5356495Sspeer 	/* Look up the "interrupts" property. */
5366495Sspeer 	if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0,
5376495Sspeer 	    "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) {
5386495Sspeer 		NXGE_ERROR_MSG((nxge, HIO_CTL,
5396495Sspeer 		    "==> nxge_hio_intr_init(obp): no 'interrupts' property"));
5406495Sspeer 		return (NXGE_ERROR);
5416495Sspeer 	}
5426495Sspeer 
5436495Sspeer 	/*
5446495Sspeer 	 * For each device assigned, the content of each interrupts
5456495Sspeer 	 * property is its logical device group.
5466495Sspeer 	 *
5476495Sspeer 	 * Assignment of interrupts property is in the the following
5486495Sspeer 	 * order:
5496495Sspeer 	 *
5506495Sspeer 	 * two receive channels
5516495Sspeer 	 * two transmit channels
5526495Sspeer 	 */
5536495Sspeer 	for (i = 0; i < prop_len; i++) {
5546495Sspeer 		hardware->ldg[i] = prop_val[i];
5556495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL,
5566495Sspeer 		    "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d",
5576495Sspeer 		    nxge->function_num, i, hardware->ldg[i]));
5586495Sspeer 	}
5596495Sspeer 	ddi_prop_free(prop_val);
5606495Sspeer 
5616495Sspeer 	hardware->max_grpids = prop_len;
5626495Sspeer 	hardware->max_ldgs = prop_len;
5636495Sspeer 	hardware->ldg_chn_start = 0;
5646495Sspeer 
5656495Sspeer 	/* ----------------------------------------------------- */
5666495Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
5676495Sspeer 
5686495Sspeer 	interrupts->intr_registered = B_FALSE;
5696495Sspeer 	interrupts->intr_enabled = B_FALSE;
5706495Sspeer 	interrupts->start_inum = 0;
5716495Sspeer 
5726495Sspeer 	ddi_status = ddi_intr_get_supported_types(
5736495Sspeer 	    nxge->dip, &interrupts->intr_types);
5746495Sspeer 	if (ddi_status != DDI_SUCCESS) {
5756495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
5766495Sspeer 		    "ddi_intr_get_supported_types() returned 0x%x, "
5776495Sspeer 		    "types = 0x%x", ddi_status, interrupts->intr_types));
5786495Sspeer 		return (NXGE_ERROR);
5796495Sspeer 	}
5806495Sspeer 
5816495Sspeer 	NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() "
5826495Sspeer 	    "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types));
5836495Sspeer 
5846495Sspeer 	/* HIOXXX hack */
5856495Sspeer 	interrupts->intr_type = DDI_INTR_TYPE_FIXED;
5866495Sspeer 	/* HIOXXX hack */
5876495Sspeer 
5886495Sspeer 	intr_type = interrupts->intr_type;
5896495Sspeer 
5906495Sspeer 	ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail);
5916495Sspeer 	if (ddi_status != DDI_SUCCESS) {
5926495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
5936495Sspeer 		    "ddi_intr_get_navail() returned %s, navail: %d",
5946495Sspeer 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
5956495Sspeer 		    "DDI_INTR_NOTFOUND", navail));
5966495Sspeer 		return (NXGE_ERROR);
5976495Sspeer 	}
5986495Sspeer 
5996495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
6006495Sspeer 	    "nxge_hio_intr_init: number of available interrupts: %d", navail));
6016495Sspeer 
6026495Sspeer 	ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs);
6036495Sspeer 	if (ddi_status != DDI_SUCCESS) {
6046495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
6056495Sspeer 		    "ddi_intr_get_nintrs() returned %s, nintrs: %d",
6066495Sspeer 		    ddi_status == DDI_FAILURE ? "DDI_FAILURE" :
6076495Sspeer 		    "DDI_INTR_NOTFOUND", nintrs));
6086495Sspeer 		return (NXGE_ERROR);
6096495Sspeer 	}
6106495Sspeer 
6116495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
6126495Sspeer 	    "nxge_hio_intr_init: number of interrupts: %d", nintrs));
6136495Sspeer 
6146495Sspeer 	interrupts->intr_size = navail * sizeof (ddi_intr_handle_t);
6156495Sspeer 	interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP);
6166495Sspeer 
6176495Sspeer 	/*
6186495Sspeer 	 * When <behavior> is set to  DDI_INTR_ALLOC_STRICT,
6196495Sspeer 	 * ddi_intr_alloc() succeeds if and only if <navail>
6206495Sspeer 	 * interrupts are are allocated. Otherwise, it fails.
6216495Sspeer 	 */
6226495Sspeer 	behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ?
6236495Sspeer 	    DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL);
6246495Sspeer 
6256495Sspeer 	ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type,
6266495Sspeer 	    inum, navail, &nactual, behavior);
6276495Sspeer 	if (ddi_status != DDI_SUCCESS) {
6286495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
6296495Sspeer 		    "ddi_intr_alloc() returned 0x%x%, "
6306495Sspeer 		    "number allocated: %d", ddi_status, nactual));
6316495Sspeer 		return (NXGE_ERROR);
6326495Sspeer 	}
6336495Sspeer 
6346495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
6356495Sspeer 	    "nxge_hio_intr_init: number of interrupts allocated: %d", nactual));
6366495Sspeer 
6376495Sspeer 	/* <ninterrupts> is a dead variable: we may as well use it. */
6386495Sspeer 	hardware->ninterrupts = nactual;
6396495Sspeer 
6406495Sspeer 	/* FOI: Get the interrupt priority. */
6416495Sspeer 	if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0],
6426495Sspeer 	    (uint_t *)&interrupts->pri)) != DDI_SUCCESS) {
6436495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
6446495Sspeer 		    " ddi_intr_get_pri() failed: %d", ddi_status));
6456495Sspeer 	}
6466495Sspeer 
6476495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
6486495Sspeer 	    "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri));
6496495Sspeer 
6506495Sspeer 	/* FOI: Get our interrupt capability flags. */
6516495Sspeer 	if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0],
6526495Sspeer 	    &interrupts->intr_cap)) != DDI_SUCCESS) {
6536495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
6546495Sspeer 		    "ddi_intr_get_cap() failed: %d", ddi_status));
6556495Sspeer 	}
6566495Sspeer 
6576495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
6586495Sspeer 	    "nxge_hio_intr_init: interrupt capabilities: %d",
6596495Sspeer 	    interrupts->intr_cap));
6606495Sspeer 
6616495Sspeer 	interrupts->intr_registered = B_TRUE;
6626495Sspeer 
6636495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init"));
6646495Sspeer 
6656495Sspeer 	return (NXGE_OK);
6666495Sspeer }
6676495Sspeer 
6686495Sspeer /*
6696495Sspeer  * nxge_hio_intr_uninit
6706495Sspeer  *
6716495Sspeer  *	Uninitialize interrupts in a guest domain.
6726495Sspeer  *
6736495Sspeer  * Arguments:
6746495Sspeer  * 	nxge
6756495Sspeer  *
6766495Sspeer  * Notes:
6776495Sspeer  *
6786495Sspeer  * Context:
6796495Sspeer  *	Guest domain
6806495Sspeer  */
6816495Sspeer void
6826495Sspeer nxge_hio_intr_uninit(
6836495Sspeer 	nxge_t *nxge)
6846495Sspeer {
6856495Sspeer 	nxge_hw_pt_cfg_t *hardware;
6866495Sspeer 	nxge_intr_t *interrupts;
6876495Sspeer 	nxge_ldgv_t *control;
6886495Sspeer 	int i;
6896495Sspeer 
6906495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit"));
6916495Sspeer 
6926495Sspeer 	/* ----------------------------------------------------- */
6936495Sspeer 	interrupts = (nxge_intr_t *)&nxge->nxge_intr_type;
6946495Sspeer 
6956495Sspeer 	/*
6966495Sspeer 	 * If necessary, disable any currently active interrupts.
6976495Sspeer 	 */
6986495Sspeer 	if (interrupts->intr_enabled) {
6996495Sspeer 		nxge_grp_set_t *set;
7006495Sspeer 		nxge_grp_t *group;
7016495Sspeer 		int channel;
7026495Sspeer 
7036495Sspeer 		set = &nxge->tx_set;
7046495Sspeer 		group = set->group[0];	/* Assumption: only one group! */
7056495Sspeer 		for (channel = 0; channel < NXGE_MAX_TDCS; channel++) {
7066495Sspeer 			if ((1 << channel) & group->map) {
7076495Sspeer 				(void) nxge_hio_intr_remove(
7086495Sspeer 				    nxge, VP_BOUND_TX, channel);
7096495Sspeer 			}
7106495Sspeer 		}
7116495Sspeer 
7126495Sspeer 		set = &nxge->rx_set;
7136495Sspeer 		group = set->group[0];	/* Assumption: only one group! */
7146495Sspeer 		for (channel = 0; channel < NXGE_MAX_RDCS; channel++) {
7156495Sspeer 			if ((1 << channel) & group->map) {
7166495Sspeer 				(void) nxge_hio_intr_remove(
7176495Sspeer 				    nxge, VP_BOUND_RX, channel);
7186495Sspeer 			}
7196495Sspeer 		}
7206495Sspeer 	}
7216495Sspeer 
7226495Sspeer 	/*
7236495Sspeer 	 * Free all of our allocated interrupts.
7246495Sspeer 	 */
7256495Sspeer 	hardware = &nxge->pt_config.hw_config;
7266495Sspeer 	for (i = 0; i < hardware->ninterrupts; i++) {
7276495Sspeer 		if (interrupts->htable[i])
7286495Sspeer 			(void) ddi_intr_free(interrupts->htable[i]);
7296495Sspeer 		interrupts->htable[i] = 0;
7306495Sspeer 	}
7316495Sspeer 
7326495Sspeer 	interrupts->intr_registered = B_FALSE;
7336495Sspeer 
7347587SMichael.Speer@Sun.COM 	if (nxge->ldgvp == NULL)
7357587SMichael.Speer@Sun.COM 		goto nxge_hio_intr_uninit_exit;
7367587SMichael.Speer@Sun.COM 
7376495Sspeer 	control = nxge->ldgvp;
7386495Sspeer 	if (control->ldgp) {
7396495Sspeer 		KMEM_FREE(control->ldgp,
7406495Sspeer 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS);
7416495Sspeer 		control->ldgp = 0;
7426495Sspeer 	}
7436495Sspeer 
7446495Sspeer 	if (control->ldvp) {
7456495Sspeer 		KMEM_FREE(control->ldvp,
7466495Sspeer 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS);
7476495Sspeer 		control->ldvp = 0;
7486495Sspeer 	}
7496495Sspeer 
7507587SMichael.Speer@Sun.COM nxge_hio_intr_uninit_exit:
7516495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit"));
7526495Sspeer }
7536495Sspeer 
7546495Sspeer /*
7556495Sspeer  * nxge_hio_tdsv_add
7566495Sspeer  *
7576495Sspeer  *	Add a transmit device interrupt.
7586495Sspeer  *
7596495Sspeer  * Arguments:
7606495Sspeer  * 	nxge
7616495Sspeer  * 	dc	The TDC whose interrupt we're adding
7626495Sspeer  *
7636495Sspeer  * Notes:
7646495Sspeer  *
7656495Sspeer  * Context:
7666495Sspeer  *	Guest domain
7676495Sspeer  */
7686495Sspeer static
7696495Sspeer hv_rv_t
7706495Sspeer nxge_hio_tdsv_add(
7716495Sspeer 	nxge_t *nxge,
7726495Sspeer 	nxge_hio_dc_t *dc)
7736495Sspeer {
7746495Sspeer 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
7756495Sspeer 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
7766495Sspeer 	nxhv_dc_fp_t *tx = &nhd->hio.tx;
7776495Sspeer 	hv_rv_t hv_rv;
7786495Sspeer 
7796495Sspeer 	if (tx->getinfo == 0) {
7806495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
7816495Sspeer 		    "nx_hio_tdsv_add: tx->getinfo absent"));
7826495Sspeer 		return (EINVAL);
7836495Sspeer 	}
7846495Sspeer 
7856495Sspeer 	/*
7866495Sspeer 	 * Get the dma channel information.
7876495Sspeer 	 */
7886495Sspeer 	hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
7896495Sspeer 	    &dc->ldg.ldsv);
7906495Sspeer 	if (hv_rv != 0) {
7916495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
7926495Sspeer 		    "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv));
7936495Sspeer 		return (EIO);
7946495Sspeer 	}
7956495Sspeer 
7966495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
7976495Sspeer 	    "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d",
7986495Sspeer 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
7996495Sspeer 
8006495Sspeer 	if (hardware->tdc.count == 0) {
8016495Sspeer 		hardware->tdc.start = dc->channel;
8026495Sspeer 	}
8036495Sspeer 
8046495Sspeer 	hardware->tdc.count++;
8056495Sspeer 	hardware->tdc.owned++;
8066495Sspeer 
8076495Sspeer 	/*
8086495Sspeer 	 * In version 1.0 of the hybrid I/O driver, there
8096495Sspeer 	 * are eight interrupt vectors per VR.
8106495Sspeer 	 *
8116495Sspeer 	 * Vectors 0 - 3 are reserved for RDCs.
8126495Sspeer 	 * Vectors 4 - 7 are reserved for TDCs.
8136495Sspeer 	 */
8146495Sspeer 	dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE;
8156495Sspeer 	// Version 1.0 hack only!
8166495Sspeer 
8176495Sspeer 	return (0);
8186495Sspeer }
8196495Sspeer 
8206495Sspeer /*
8216495Sspeer  * nxge_hio_rdsv_add
8226495Sspeer  *
8236495Sspeer  *	Add a transmit device interrupt.
8246495Sspeer  *
8256495Sspeer  * Arguments:
8266495Sspeer  * 	nxge
8276495Sspeer  * 	dc	The RDC whose interrupt we're adding
8286495Sspeer  *
8296495Sspeer  * Notes:
8306495Sspeer  *
8316495Sspeer  * Context:
8326495Sspeer  *	Guest domain
8336495Sspeer  */
8346495Sspeer static
8356495Sspeer hv_rv_t
8366495Sspeer nxge_hio_rdsv_add(
8376495Sspeer 	nxge_t *nxge,
8386495Sspeer 	nxge_hio_dc_t *dc)
8396495Sspeer {
8406495Sspeer 	nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio;
8416495Sspeer 	nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config;
8426495Sspeer 	nxhv_dc_fp_t *rx = &nhd->hio.rx;
8436495Sspeer 	hv_rv_t hv_rv;
8446495Sspeer 
8456495Sspeer 	if (rx->getinfo == 0) {
8466495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
8476495Sspeer 		    "nx_hio_tdsv_add: rx->getinfo absent"));
8486495Sspeer 		return (EINVAL);
8496495Sspeer 	}
8506495Sspeer 
8516495Sspeer 	/*
8526495Sspeer 	 * Get DMA channel information.
8536495Sspeer 	 */
8546495Sspeer 	hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index,
8556495Sspeer 	    &dc->ldg.ldsv);
8566495Sspeer 	if (hv_rv != 0) {
8576495Sspeer 		NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL,
8586495Sspeer 		    "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv));
8596495Sspeer 		return (EIO);
8606495Sspeer 	}
8616495Sspeer 
8626495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL,
8636495Sspeer 	    "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d",
8646495Sspeer 	    (int)dc->ldg.index, (int)dc->ldg.ldsv));
8656495Sspeer 
8666495Sspeer 	if (hardware->max_rdcs == 0) {
8676495Sspeer 		hardware->start_rdc = dc->channel;
8686495Sspeer 		hardware->def_rdc = dc->channel;
8696495Sspeer 	}
8706495Sspeer 
8716495Sspeer 	hardware->max_rdcs++;
8726495Sspeer 
8736495Sspeer 	/*
8746495Sspeer 	 * In version 1.0 of the hybrid I/O driver, there
8756495Sspeer 	 * are eight interrupt vectors per VR.
8766495Sspeer 	 *
8776495Sspeer 	 * Vectors 0 - 3 are reserved for RDCs.
8786495Sspeer 	 */
8796495Sspeer 	dc->ldg.vector = (dc->ldg.ldsv % 2);
8806495Sspeer 	// Version 1.0 hack only!
8816495Sspeer 
8826495Sspeer 	return (0);
8836495Sspeer }
8846495Sspeer 
8856495Sspeer /*
8866495Sspeer  * nxge_hio_ldsv_add
8876495Sspeer  *
8886495Sspeer  *	Add a transmit or receive interrupt.
8896495Sspeer  *
8906495Sspeer  * Arguments:
8916495Sspeer  * 	nxge
8926495Sspeer  * 	dc	The DMA channel whose interrupt we're adding
8936495Sspeer  *
8946495Sspeer  * Notes:
8956495Sspeer  *	Guest domains can only add interrupts for DMA channels.
8966495Sspeer  *	They cannot access the MAC, MIF, or SYSERR interrupts.
8976495Sspeer  *
8986495Sspeer  * Context:
8996495Sspeer  *	Guest domain
9006495Sspeer  */
9016495Sspeer hv_rv_t
9026495Sspeer nxge_hio_ldsv_add(
9036495Sspeer 	nxge_t *nxge,
9046495Sspeer 	nxge_hio_dc_t *dc)
9056495Sspeer {
9066495Sspeer 	nxge_ldgv_t *control;
9076495Sspeer 	nxge_ldg_t *group;
9086495Sspeer 	nxge_ldv_t *device;
9096495Sspeer 	hv_rv_t hv_rv;
9106495Sspeer 
9116495Sspeer 	if (dc->type == VP_BOUND_TX) {
9126495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)",
9136495Sspeer 		    dc->channel));
9146495Sspeer 		if ((hv_rv = nxge_hio_tdsv_add(nxge, dc)) != 0)
9156495Sspeer 			return (hv_rv);
9166495Sspeer 	} else {
9176495Sspeer 		NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)",
9186495Sspeer 		    dc->channel));
9196495Sspeer 		if ((hv_rv = nxge_hio_rdsv_add(nxge, dc)) != 0)
9206495Sspeer 			return (hv_rv);
9216495Sspeer 	}
9226495Sspeer 
9236495Sspeer 	dc->ldg.map |= (1 << dc->ldg.ldsv);
9246495Sspeer 
9256495Sspeer 	control = nxge->ldgvp;
9266495Sspeer 	if (control == NULL) {
9276495Sspeer 		control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP);
9286495Sspeer 		nxge->ldgvp = control;
9296495Sspeer 		control->maxldgs = 1;
9306495Sspeer 		control->maxldvs = 1;
9316495Sspeer 		control->ldgp = KMEM_ZALLOC(
9326495Sspeer 		    sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP);
9336495Sspeer 		control->ldvp = KMEM_ZALLOC(
9346495Sspeer 		    sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP);
9356495Sspeer 	} else {
9366495Sspeer 		control->maxldgs++;
9376495Sspeer 		control->maxldvs++;
9386495Sspeer 	}
9396495Sspeer 
9406495Sspeer 	/*
9416495Sspeer 	 * Initialize the logical device group data structure first.
9426495Sspeer 	 */
9436495Sspeer 	group = &control->ldgp[dc->ldg.vector];
9446495Sspeer 
9456495Sspeer 	(void) memset(group, 0, sizeof (*group));
9466495Sspeer 
9476495Sspeer 	/*
9486495Sspeer 	 * <hw_config.ldg> is a copy of the "interrupts" property.
9496495Sspeer 	 */
9506495Sspeer 	group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector];
9516495Sspeer 	group->vldg_index = (uint8_t)dc->ldg.index;
9526495Sspeer 	/*
9536495Sspeer 	 * Since <vldg_index> is a dead variable, I'm reusing
9546495Sspeer 	 * it in Hybrid I/O to calculate the offset into the
9556495Sspeer 	 * virtual PIO_LDSV space.
9566495Sspeer 	 */
9576495Sspeer 
9586495Sspeer 	group->arm = B_TRUE;
9596495Sspeer 	group->ldg_timer = NXGE_TIMER_LDG;
9606495Sspeer 	group->func = nxge->function_num;
9616495Sspeer 	group->vector = dc->ldg.vector;
9626495Sspeer 	/*
9636495Sspeer 	 * <intdata> appears to be a dead variable.
9646495Sspeer 	 * Though it is not used anywhere in the driver,
9656495Sspeer 	 * we'll set it anyway.
9666495Sspeer 	 */
9676495Sspeer 	group->intdata = SID_DATA(group->func, group->vector);
9686495Sspeer 
9696495Sspeer 	group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */
9706495Sspeer 	group->nxgep = nxge;
9716495Sspeer 
9726495Sspeer 	/*
9736495Sspeer 	 * Initialize the logical device state vector next.
9746495Sspeer 	 */
9756495Sspeer 	device = &control->ldvp[dc->ldg.ldsv];
9766495Sspeer 
9776495Sspeer 	device->ldg_assigned = group->ldg;
9786495Sspeer 	device->ldv = dc->ldg.ldsv;
9796495Sspeer 
9806495Sspeer 	if (dc->type == VP_BOUND_TX) {
9816495Sspeer 		device->is_txdma = B_TRUE;
9826495Sspeer 		device->is_rxdma = B_FALSE;
9836495Sspeer 		device->ldv_intr_handler = nxge_tx_intr;
9846495Sspeer 	} else {
9856495Sspeer 		device->is_rxdma = B_TRUE;
9866495Sspeer 		device->is_txdma = B_FALSE;
9876495Sspeer 		device->ldv_intr_handler = nxge_rx_intr;
9886495Sspeer 	}
9896495Sspeer 	device->is_mif = B_FALSE;
9906495Sspeer 	device->is_mac = B_FALSE;
9916495Sspeer 	device->is_syserr = B_FALSE;
9926495Sspeer 	device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */
9936495Sspeer 
9946495Sspeer 	device->channel = dc->channel;
9956495Sspeer 	device->vdma_index = dc->page;
9966495Sspeer 	device->func = nxge->function_num;
9976495Sspeer 	device->ldgp = group;
9986495Sspeer 	device->ldv_flags = 0;
9996495Sspeer 	device->ldv_ldf_masks = 0;
10006495Sspeer 
10016495Sspeer 	device->nxgep = nxge;
10026495Sspeer 
10036495Sspeer 	/*
10046495Sspeer 	 * This code seems to imply a strict 1-to-1 correspondence.
10056495Sspeer 	 */
10066495Sspeer 	group->nldvs++;
10076495Sspeer 	group->ldvp = device;
10086495Sspeer 
10096495Sspeer 	control->nldvs++;
10106495Sspeer 	control->ldg_intrs++;
10116495Sspeer 
10126495Sspeer 	NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add"));
10136495Sspeer 
10146495Sspeer 	return (0);
10156495Sspeer }
10166495Sspeer 
10176495Sspeer /*
10186495Sspeer  * nxge_hio_ldsv_im
10196495Sspeer  *
10206495Sspeer  *	Manage a VLDG's interrupts.
10216495Sspeer  *
10226495Sspeer  * Arguments:
10236495Sspeer  * 	nxge
10246495Sspeer  * 	group	The VLDG to manage
10256495Sspeer  *
10266495Sspeer  * Notes:
10276495Sspeer  *	There are 8 sets of 4 64-bit registers per VR, 1 per LDG.
10286495Sspeer  *	That sums to 256 bytes of virtual PIO_LDSV space.
10296495Sspeer  *
10306495Sspeer  *		VLDG0 starts at offset 0,
10316495Sspeer  *		VLDG1 starts at offset 32, etc.
10326495Sspeer  *
10336495Sspeer  *	Each set consists of 4 registers:
10346495Sspeer  *		Logical Device State Vector 0. LDSV0
10356495Sspeer  *		Logical Device State Vector 1. LDSV1
10366495Sspeer  *		Logical Device State Vector 2. LDSV2
10376495Sspeer  *		Logical Device Group Interrupt Management. LDGIMGN
10386495Sspeer  *
10396495Sspeer  *	The first three (LDSVx) are read-only.  The 4th register is the
10406495Sspeer  *	LDGIMGN, the LDG Interrupt Management register, which is used to
10416495Sspeer  *	arm the LDG, or set its timer.
10426495Sspeer  *
10436495Sspeer  *	The offset to write to is calculated as follows:
10446495Sspeer  *
10456495Sspeer  *		0x2000 + (VLDG << 4) + offset, where:
10466495Sspeer  *		VDLG is the virtual group, i.e., index of the LDG.
10476495Sspeer  *		offset is the offset (alignment 8) of the register
10486495Sspeer  *		       to read or write.
10496495Sspeer  *
10506495Sspeer  *	So, for example, if we wanted to arm the first TDC of VRx, we would
10516495Sspeer  *	calculate the address as:
10526495Sspeer  *
10536495Sspeer  *	0x2000 + (0 << 4) + 0x18 = 0x18
10546495Sspeer  *
10556495Sspeer  * Context:
10566495Sspeer  *	Guest domain
10576495Sspeer  *
10586495Sspeer  */
10596495Sspeer void
10606495Sspeer nxge_hio_ldsv_im(
10616495Sspeer 	/* Read any register in the PIO_LDSV space. */
10626495Sspeer 	nxge_t *nxge,
10636495Sspeer 	nxge_ldg_t *group,
10646495Sspeer 	pio_ld_op_t op,
10656495Sspeer 	uint64_t *value)
10666495Sspeer {
10676495Sspeer 	uint64_t offset = VLDG_OFFSET;
10686495Sspeer 
10696495Sspeer 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
10706495Sspeer 	offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */
10716495Sspeer 
10726495Sspeer 	NXGE_REG_RD64(nxge->npi_handle, offset, value);
10736495Sspeer }
10746495Sspeer 
10756495Sspeer void
10766495Sspeer nxge_hio_ldgimgn(
10776495Sspeer 	/* Write the PIO_LDGIMGN register. */
10786495Sspeer 	nxge_t *nxge,
10796495Sspeer 	nxge_ldg_t *group)
10806495Sspeer {
10816495Sspeer 	uint64_t offset = VLDG_OFFSET;
10826495Sspeer 	ldgimgm_t mgm;
10836495Sspeer 
10846495Sspeer 	offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */
10856495Sspeer 	offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */
10866495Sspeer 
10876495Sspeer 	mgm.value = 0;
10886495Sspeer 	if (group->arm) {
10896495Sspeer 		mgm.bits.ldw.arm = 1;
10906495Sspeer 		mgm.bits.ldw.timer = group->ldg_timer;
10916495Sspeer 	} else {
10926495Sspeer 		mgm.bits.ldw.arm = 0;
10936495Sspeer 		mgm.bits.ldw.timer = 0;
10946495Sspeer 	}
10956495Sspeer 	NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value);
10966495Sspeer }
1097