1*49ef7e06SGarrett D'Amore /*
2*49ef7e06SGarrett D'Amore  * Copyright (c) 2008-2016 Solarflare Communications Inc.
3*49ef7e06SGarrett D'Amore  * All rights reserved.
4*49ef7e06SGarrett D'Amore  *
5*49ef7e06SGarrett D'Amore  * Redistribution and use in source and binary forms, with or without
6*49ef7e06SGarrett D'Amore  * modification, are permitted provided that the following conditions are met:
7*49ef7e06SGarrett D'Amore  *
8*49ef7e06SGarrett D'Amore  * 1. Redistributions of source code must retain the above copyright notice,
9*49ef7e06SGarrett D'Amore  *    this list of conditions and the following disclaimer.
10*49ef7e06SGarrett D'Amore  * 2. Redistributions in binary form must reproduce the above copyright notice,
11*49ef7e06SGarrett D'Amore  *    this list of conditions and the following disclaimer in the documentation
12*49ef7e06SGarrett D'Amore  *    and/or other materials provided with the distribution.
13*49ef7e06SGarrett D'Amore  *
14*49ef7e06SGarrett D'Amore  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15*49ef7e06SGarrett D'Amore  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16*49ef7e06SGarrett D'Amore  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17*49ef7e06SGarrett D'Amore  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18*49ef7e06SGarrett D'Amore  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19*49ef7e06SGarrett D'Amore  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20*49ef7e06SGarrett D'Amore  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21*49ef7e06SGarrett D'Amore  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22*49ef7e06SGarrett D'Amore  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23*49ef7e06SGarrett D'Amore  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24*49ef7e06SGarrett D'Amore  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*49ef7e06SGarrett D'Amore  *
26*49ef7e06SGarrett D'Amore  * The views and conclusions contained in the software and documentation are
27*49ef7e06SGarrett D'Amore  * those of the authors and should not be interpreted as representing official
28*49ef7e06SGarrett D'Amore  * policies, either expressed or implied, of the FreeBSD Project.
29*49ef7e06SGarrett D'Amore  */
30*49ef7e06SGarrett D'Amore 
31*49ef7e06SGarrett D'Amore #include <sys/types.h>
32*49ef7e06SGarrett D'Amore #include <sys/ddi.h>
33*49ef7e06SGarrett D'Amore #include <sys/sunddi.h>
34*49ef7e06SGarrett D'Amore #include <sys/atomic.h>
35*49ef7e06SGarrett D'Amore #include <sys/modctl.h>
36*49ef7e06SGarrett D'Amore #include <sys/conf.h>
37*49ef7e06SGarrett D'Amore #include <sys/ethernet.h>
38*49ef7e06SGarrett D'Amore #include <sys/pci.h>
39*49ef7e06SGarrett D'Amore #include <sys/pcie.h>
40*49ef7e06SGarrett D'Amore 
41*49ef7e06SGarrett D'Amore #include "sfxge.h"
42*49ef7e06SGarrett D'Amore 
43*49ef7e06SGarrett D'Amore #include "efx.h"
44*49ef7e06SGarrett D'Amore 
45*49ef7e06SGarrett D'Amore 
46*49ef7e06SGarrett D'Amore /* Interrupt table DMA attributes */
47*49ef7e06SGarrett D'Amore static ddi_device_acc_attr_t sfxge_intr_devacc = {
48*49ef7e06SGarrett D'Amore 
49*49ef7e06SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
50*49ef7e06SGarrett D'Amore 	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
51*49ef7e06SGarrett D'Amore 	DDI_STRICTORDER_ACC	/* devacc_attr_dataorder */
52*49ef7e06SGarrett D'Amore };
53*49ef7e06SGarrett D'Amore 
54*49ef7e06SGarrett D'Amore static ddi_dma_attr_t sfxge_intr_dma_attr = {
55*49ef7e06SGarrett D'Amore 	DMA_ATTR_V0,		/* dma_attr_version	*/
56*49ef7e06SGarrett D'Amore 	0,			/* dma_attr_addr_lo	*/
57*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_addr_hi	*/
58*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_count_max	*/
59*49ef7e06SGarrett D'Amore 	EFX_INTR_SIZE,		/* dma_attr_align	*/
60*49ef7e06SGarrett D'Amore 	0xffffffff,		/* dma_attr_burstsizes	*/
61*49ef7e06SGarrett D'Amore 	1,			/* dma_attr_minxfer	*/
62*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_maxxfer	*/
63*49ef7e06SGarrett D'Amore 	0xffffffffffffffffull,	/* dma_attr_seg		*/
64*49ef7e06SGarrett D'Amore 	1,			/* dma_attr_sgllen	*/
65*49ef7e06SGarrett D'Amore 	1,			/* dma_attr_granular	*/
66*49ef7e06SGarrett D'Amore 	0			/* dma_attr_flags	*/
67*49ef7e06SGarrett D'Amore };
68*49ef7e06SGarrett D'Amore 
69*49ef7e06SGarrett D'Amore static unsigned int
sfxge_intr_line(caddr_t arg1,caddr_t arg2)70*49ef7e06SGarrett D'Amore sfxge_intr_line(caddr_t arg1, caddr_t arg2)
71*49ef7e06SGarrett D'Amore {
72*49ef7e06SGarrett D'Amore 	sfxge_t *sp = (void *)arg1;
73*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
74*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
75*49ef7e06SGarrett D'Amore 	unsigned int index;
76*49ef7e06SGarrett D'Amore 	boolean_t fatal;
77*49ef7e06SGarrett D'Amore 	uint32_t qmask;
78*49ef7e06SGarrett D'Amore 	int rc;
79*49ef7e06SGarrett D'Amore 
80*49ef7e06SGarrett D'Amore 	_NOTE(ARGUNUSED(arg2))
81*49ef7e06SGarrett D'Amore 
82*49ef7e06SGarrett D'Amore 	ASSERT3U(sip->si_type, ==, EFX_INTR_LINE);
83*49ef7e06SGarrett D'Amore 
84*49ef7e06SGarrett D'Amore 	if (sip->si_state != SFXGE_INTR_STARTED &&
85*49ef7e06SGarrett D'Amore 	    sip->si_state != SFXGE_INTR_TESTING) {
86*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_UNCLAIMED;
87*49ef7e06SGarrett D'Amore 		goto done;
88*49ef7e06SGarrett D'Amore 	}
89*49ef7e06SGarrett D'Amore 
90*49ef7e06SGarrett D'Amore 	if (sip->si_state == SFXGE_INTR_TESTING) {
91*49ef7e06SGarrett D'Amore 		sip->si_mask |= 1;	/* only one interrupt */
92*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_CLAIMED;
93*49ef7e06SGarrett D'Amore 		goto done;
94*49ef7e06SGarrett D'Amore 	}
95*49ef7e06SGarrett D'Amore 
96*49ef7e06SGarrett D'Amore 	efx_intr_status_line(enp, &fatal, &qmask);
97*49ef7e06SGarrett D'Amore 
98*49ef7e06SGarrett D'Amore 	if (fatal) {
99*49ef7e06SGarrett D'Amore 		sfxge_intr_fatal(sp);
100*49ef7e06SGarrett D'Amore 
101*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_CLAIMED;
102*49ef7e06SGarrett D'Amore 		goto done;
103*49ef7e06SGarrett D'Amore 	}
104*49ef7e06SGarrett D'Amore 
105*49ef7e06SGarrett D'Amore 	if (qmask != 0) {
106*49ef7e06SGarrett D'Amore 		for (index = 0; index < EFX_INTR_NEVQS; index++) {
107*49ef7e06SGarrett D'Amore 			if (qmask & (1 << index))
108*49ef7e06SGarrett D'Amore 				(void) sfxge_ev_qpoll(sp, index);
109*49ef7e06SGarrett D'Amore 		}
110*49ef7e06SGarrett D'Amore 
111*49ef7e06SGarrett D'Amore 		sip->si_zero_count = 0;
112*49ef7e06SGarrett D'Amore 		sfxge_gld_rx_push(sp);
113*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_CLAIMED;
114*49ef7e06SGarrett D'Amore 		goto done;
115*49ef7e06SGarrett D'Amore 	}
116*49ef7e06SGarrett D'Amore 
117*49ef7e06SGarrett D'Amore 	/*
118*49ef7e06SGarrett D'Amore 	 * bug15671/bug17203 workaround. Return CLAIMED for the first ISR=0
119*49ef7e06SGarrett D'Amore 	 * interrupt, and poll all evqs for work. For subsequent ISR=0
120*49ef7e06SGarrett D'Amore 	 * interrupts (the line must be shared in this case), just rearm the
121*49ef7e06SGarrett D'Amore 	 * event queues to ensure we don't miss an interrupt.
122*49ef7e06SGarrett D'Amore 	 */
123*49ef7e06SGarrett D'Amore 	if (sip->si_zero_count++ == 0) {
124*49ef7e06SGarrett D'Amore 		for (index = 0; index < EFX_INTR_NEVQS; index++) {
125*49ef7e06SGarrett D'Amore 			if (sp->s_sep[index] != NULL)
126*49ef7e06SGarrett D'Amore 				(void) sfxge_ev_qpoll(sp, index);
127*49ef7e06SGarrett D'Amore 		}
128*49ef7e06SGarrett D'Amore 
129*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_CLAIMED;
130*49ef7e06SGarrett D'Amore 	} else {
131*49ef7e06SGarrett D'Amore 		for (index = 0; index < EFX_INTR_NEVQS; index++) {
132*49ef7e06SGarrett D'Amore 			if (sp->s_sep[index] != NULL)
133*49ef7e06SGarrett D'Amore 				(void) sfxge_ev_qprime(sp, index);
134*49ef7e06SGarrett D'Amore 		}
135*49ef7e06SGarrett D'Amore 
136*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_UNCLAIMED;
137*49ef7e06SGarrett D'Amore 	}
138*49ef7e06SGarrett D'Amore 
139*49ef7e06SGarrett D'Amore done:
140*49ef7e06SGarrett D'Amore 	return (rc);
141*49ef7e06SGarrett D'Amore }
142*49ef7e06SGarrett D'Amore 
143*49ef7e06SGarrett D'Amore static unsigned int
sfxge_intr_message(caddr_t arg1,caddr_t arg2)144*49ef7e06SGarrett D'Amore sfxge_intr_message(caddr_t arg1, caddr_t arg2)
145*49ef7e06SGarrett D'Amore {
146*49ef7e06SGarrett D'Amore 	sfxge_t *sp = (void *)arg1;
147*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
148*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
149*49ef7e06SGarrett D'Amore 	unsigned int index = (unsigned int)(uintptr_t)arg2;
150*49ef7e06SGarrett D'Amore 	boolean_t fatal;
151*49ef7e06SGarrett D'Amore 	int rc;
152*49ef7e06SGarrett D'Amore 
153*49ef7e06SGarrett D'Amore 	ASSERT3U(sip->si_type, ==, EFX_INTR_MESSAGE);
154*49ef7e06SGarrett D'Amore 
155*49ef7e06SGarrett D'Amore 	if (sip->si_state != SFXGE_INTR_STARTED &&
156*49ef7e06SGarrett D'Amore 	    sip->si_state != SFXGE_INTR_TESTING) {
157*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_UNCLAIMED;
158*49ef7e06SGarrett D'Amore 		goto done;
159*49ef7e06SGarrett D'Amore 	}
160*49ef7e06SGarrett D'Amore 
161*49ef7e06SGarrett D'Amore 	if (sip->si_state == SFXGE_INTR_TESTING) {
162*49ef7e06SGarrett D'Amore 		uint64_t mask;
163*49ef7e06SGarrett D'Amore 
164*49ef7e06SGarrett D'Amore 		do {
165*49ef7e06SGarrett D'Amore 			mask = sip->si_mask;
166*49ef7e06SGarrett D'Amore 		} while (atomic_cas_64(&(sip->si_mask), mask,
167*49ef7e06SGarrett D'Amore 		    mask | (1 << index)) != mask);
168*49ef7e06SGarrett D'Amore 
169*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_CLAIMED;
170*49ef7e06SGarrett D'Amore 		goto done;
171*49ef7e06SGarrett D'Amore 	}
172*49ef7e06SGarrett D'Amore 
173*49ef7e06SGarrett D'Amore 	efx_intr_status_message(enp, index, &fatal);
174*49ef7e06SGarrett D'Amore 
175*49ef7e06SGarrett D'Amore 	if (fatal) {
176*49ef7e06SGarrett D'Amore 		sfxge_intr_fatal(sp);
177*49ef7e06SGarrett D'Amore 
178*49ef7e06SGarrett D'Amore 		rc = DDI_INTR_CLAIMED;
179*49ef7e06SGarrett D'Amore 		goto done;
180*49ef7e06SGarrett D'Amore 	}
181*49ef7e06SGarrett D'Amore 
182*49ef7e06SGarrett D'Amore 	(void) sfxge_ev_qpoll(sp, index);
183*49ef7e06SGarrett D'Amore 
184*49ef7e06SGarrett D'Amore 	sfxge_gld_rx_push(sp);
185*49ef7e06SGarrett D'Amore 	rc = DDI_INTR_CLAIMED;
186*49ef7e06SGarrett D'Amore 
187*49ef7e06SGarrett D'Amore done:
188*49ef7e06SGarrett D'Amore 	return (rc);
189*49ef7e06SGarrett D'Amore }
190*49ef7e06SGarrett D'Amore 
191*49ef7e06SGarrett D'Amore static int
sfxge_intr_bus_enable(sfxge_t * sp)192*49ef7e06SGarrett D'Amore sfxge_intr_bus_enable(sfxge_t *sp)
193*49ef7e06SGarrett D'Amore {
194*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
195*49ef7e06SGarrett D'Amore 	ddi_intr_handler_t *handler;
196*49ef7e06SGarrett D'Amore 	int add_index;
197*49ef7e06SGarrett D'Amore 	int en_index;
198*49ef7e06SGarrett D'Amore 	int err;
199*49ef7e06SGarrett D'Amore 	int rc;
200*49ef7e06SGarrett D'Amore 
201*49ef7e06SGarrett D'Amore 	/* Serialise all instances to avoid problems seen in bug31184. */
202*49ef7e06SGarrett D'Amore 	mutex_enter(&sfxge_global_lock);
203*49ef7e06SGarrett D'Amore 
204*49ef7e06SGarrett D'Amore 	switch (sip->si_type) {
205*49ef7e06SGarrett D'Amore 	case EFX_INTR_MESSAGE:
206*49ef7e06SGarrett D'Amore 		handler = sfxge_intr_message;
207*49ef7e06SGarrett D'Amore 		break;
208*49ef7e06SGarrett D'Amore 
209*49ef7e06SGarrett D'Amore 	case EFX_INTR_LINE:
210*49ef7e06SGarrett D'Amore 		handler = sfxge_intr_line;
211*49ef7e06SGarrett D'Amore 		break;
212*49ef7e06SGarrett D'Amore 
213*49ef7e06SGarrett D'Amore 	default:
214*49ef7e06SGarrett D'Amore 		dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
215*49ef7e06SGarrett D'Amore 		    "bus_enable: unknown intr type (si_type=%d nalloc=%d)",
216*49ef7e06SGarrett D'Amore 		    sip->si_type, sip->si_nalloc);
217*49ef7e06SGarrett D'Amore 		ASSERT(B_FALSE);
218*49ef7e06SGarrett D'Amore 		rc = EINVAL;
219*49ef7e06SGarrett D'Amore 		goto fail1;
220*49ef7e06SGarrett D'Amore 	}
221*49ef7e06SGarrett D'Amore 
222*49ef7e06SGarrett D'Amore 	/* Try to add the handlers */
223*49ef7e06SGarrett D'Amore 	for (add_index = 0; add_index < sip->si_nalloc; add_index++) {
224*49ef7e06SGarrett D'Amore 		unsigned int pri;
225*49ef7e06SGarrett D'Amore 
226*49ef7e06SGarrett D'Amore 		/* This cannot fail unless given invalid inputs. */
227*49ef7e06SGarrett D'Amore 		err = ddi_intr_get_pri(sip->si_table[add_index], &pri);
228*49ef7e06SGarrett D'Amore 		ASSERT(err == DDI_SUCCESS);
229*49ef7e06SGarrett D'Amore 
230*49ef7e06SGarrett D'Amore 		DTRACE_PROBE2(pri, unsigned int, add_index, unsigned int, pri);
231*49ef7e06SGarrett D'Amore 
232*49ef7e06SGarrett D'Amore 		err = ddi_intr_add_handler(sip->si_table[add_index], handler,
233*49ef7e06SGarrett D'Amore 		    (caddr_t)sp, (caddr_t)(uintptr_t)add_index);
234*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
235*49ef7e06SGarrett D'Amore 			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
236*49ef7e06SGarrett D'Amore 			    "bus_enable: ddi_intr_add_handler failed"
237*49ef7e06SGarrett D'Amore 			    " err=%d (h=%p idx=%d nalloc=%d)",
238*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table[add_index], add_index,
239*49ef7e06SGarrett D'Amore 			    sip->si_nalloc);
240*49ef7e06SGarrett D'Amore 
241*49ef7e06SGarrett D'Amore 			rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
242*49ef7e06SGarrett D'Amore 			goto fail2;
243*49ef7e06SGarrett D'Amore 		}
244*49ef7e06SGarrett D'Amore 	}
245*49ef7e06SGarrett D'Amore 
246*49ef7e06SGarrett D'Amore 	/* Get interrupt capabilities */
247*49ef7e06SGarrett D'Amore 	err = ddi_intr_get_cap(sip->si_table[0], &(sip->si_cap));
248*49ef7e06SGarrett D'Amore 	if (err != DDI_SUCCESS) {
249*49ef7e06SGarrett D'Amore 		dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
250*49ef7e06SGarrett D'Amore 		    "bus_enable: ddi_intr_get_cap failed"
251*49ef7e06SGarrett D'Amore 		    " err=%d (h=%p idx=%d nalloc=%d)",
252*49ef7e06SGarrett D'Amore 		    err, (void *)sip->si_table[0], 0, sip->si_nalloc);
253*49ef7e06SGarrett D'Amore 
254*49ef7e06SGarrett D'Amore 		if (err == DDI_EINVAL)
255*49ef7e06SGarrett D'Amore 			rc = EINVAL;
256*49ef7e06SGarrett D'Amore 		else if (err == DDI_ENOTSUP)
257*49ef7e06SGarrett D'Amore 			rc = ENOTSUP;
258*49ef7e06SGarrett D'Amore 		else
259*49ef7e06SGarrett D'Amore 			rc = EFAULT;
260*49ef7e06SGarrett D'Amore 
261*49ef7e06SGarrett D'Amore 		goto fail3;
262*49ef7e06SGarrett D'Amore 	}
263*49ef7e06SGarrett D'Amore 
264*49ef7e06SGarrett D'Amore 	/* Enable interrupts at the bus  */
265*49ef7e06SGarrett D'Amore 	if (sip->si_cap & DDI_INTR_FLAG_BLOCK) {
266*49ef7e06SGarrett D'Amore 		en_index = 0; /* Silence gcc */
267*49ef7e06SGarrett D'Amore 		err = ddi_intr_block_enable(sip->si_table, sip->si_nalloc);
268*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
269*49ef7e06SGarrett D'Amore 			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
270*49ef7e06SGarrett D'Amore 			    "bus_enable: ddi_intr_block_enable failed"
271*49ef7e06SGarrett D'Amore 			    " err=%d (table=%p nalloc=%d)",
272*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table, sip->si_nalloc);
273*49ef7e06SGarrett D'Amore 
274*49ef7e06SGarrett D'Amore 			rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
275*49ef7e06SGarrett D'Amore 			goto fail4;
276*49ef7e06SGarrett D'Amore 		}
277*49ef7e06SGarrett D'Amore 	} else {
278*49ef7e06SGarrett D'Amore 		for (en_index = 0; en_index < sip->si_nalloc; en_index++) {
279*49ef7e06SGarrett D'Amore 			err = ddi_intr_enable(sip->si_table[en_index]);
280*49ef7e06SGarrett D'Amore 			if (err != DDI_SUCCESS) {
281*49ef7e06SGarrett D'Amore 				dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
282*49ef7e06SGarrett D'Amore 				    "bus_enable: ddi_intr_enable failed"
283*49ef7e06SGarrett D'Amore 				    " err=%d (h=%p idx=%d nalloc=%d)",
284*49ef7e06SGarrett D'Amore 				    err, (void *)sip->si_table[en_index],
285*49ef7e06SGarrett D'Amore 				    en_index, sip->si_nalloc);
286*49ef7e06SGarrett D'Amore 
287*49ef7e06SGarrett D'Amore 				rc = (err == DDI_EINVAL) ? EINVAL : EFAULT;
288*49ef7e06SGarrett D'Amore 				goto fail4;
289*49ef7e06SGarrett D'Amore 			}
290*49ef7e06SGarrett D'Amore 		}
291*49ef7e06SGarrett D'Amore 	}
292*49ef7e06SGarrett D'Amore 
293*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
294*49ef7e06SGarrett D'Amore 	return (0);
295*49ef7e06SGarrett D'Amore 
296*49ef7e06SGarrett D'Amore fail4:
297*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail4);
298*49ef7e06SGarrett D'Amore 
299*49ef7e06SGarrett D'Amore 	/* Disable the enabled handlers */
300*49ef7e06SGarrett D'Amore 	if (!(sip->si_cap & DDI_INTR_FLAG_BLOCK)) {
301*49ef7e06SGarrett D'Amore 		while (--en_index >= 0) {
302*49ef7e06SGarrett D'Amore 			err = ddi_intr_disable(sip->si_table[en_index]);
303*49ef7e06SGarrett D'Amore 			if (err != DDI_SUCCESS) {
304*49ef7e06SGarrett D'Amore 				dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
305*49ef7e06SGarrett D'Amore 				    "bus_enable: ddi_intr_disable"
306*49ef7e06SGarrett D'Amore 				    " failed err=%d (h=%p idx=%d nalloc=%d)",
307*49ef7e06SGarrett D'Amore 				    err, (void *)sip->si_table[en_index],
308*49ef7e06SGarrett D'Amore 				    en_index, sip->si_nalloc);
309*49ef7e06SGarrett D'Amore 			}
310*49ef7e06SGarrett D'Amore 		}
311*49ef7e06SGarrett D'Amore 	}
312*49ef7e06SGarrett D'Amore 
313*49ef7e06SGarrett D'Amore fail3:
314*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail3);
315*49ef7e06SGarrett D'Amore 
316*49ef7e06SGarrett D'Amore 	/* Remove all handlers */
317*49ef7e06SGarrett D'Amore 	add_index = sip->si_nalloc;
318*49ef7e06SGarrett D'Amore 
319*49ef7e06SGarrett D'Amore fail2:
320*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
321*49ef7e06SGarrett D'Amore 
322*49ef7e06SGarrett D'Amore 	/* Remove remaining handlers */
323*49ef7e06SGarrett D'Amore 	while (--add_index >= 0) {
324*49ef7e06SGarrett D'Amore 		err = ddi_intr_remove_handler(sip->si_table[add_index]);
325*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
326*49ef7e06SGarrett D'Amore 			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
327*49ef7e06SGarrett D'Amore 			    "bus_enable: ddi_intr_remove_handler"
328*49ef7e06SGarrett D'Amore 			    " failed err=%d (h=%p idx=%d nalloc=%d)",
329*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table[add_index], add_index,
330*49ef7e06SGarrett D'Amore 			    sip->si_nalloc);
331*49ef7e06SGarrett D'Amore 		}
332*49ef7e06SGarrett D'Amore 	}
333*49ef7e06SGarrett D'Amore 
334*49ef7e06SGarrett D'Amore fail1:
335*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
336*49ef7e06SGarrett D'Amore 
337*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
338*49ef7e06SGarrett D'Amore 	return (rc);
339*49ef7e06SGarrett D'Amore }
340*49ef7e06SGarrett D'Amore 
341*49ef7e06SGarrett D'Amore static void
sfxge_intr_bus_disable(sfxge_t * sp)342*49ef7e06SGarrett D'Amore sfxge_intr_bus_disable(sfxge_t *sp)
343*49ef7e06SGarrett D'Amore {
344*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
345*49ef7e06SGarrett D'Amore 	int index;
346*49ef7e06SGarrett D'Amore 	int err;
347*49ef7e06SGarrett D'Amore 
348*49ef7e06SGarrett D'Amore 	/* Serialise all instances to avoid problems seen in bug31184. */
349*49ef7e06SGarrett D'Amore 	mutex_enter(&sfxge_global_lock);
350*49ef7e06SGarrett D'Amore 
351*49ef7e06SGarrett D'Amore 	/* Disable interrupts at the bus */
352*49ef7e06SGarrett D'Amore 	if (sip->si_cap & DDI_INTR_FLAG_BLOCK) {
353*49ef7e06SGarrett D'Amore 		err = ddi_intr_block_disable(sip->si_table, sip->si_nalloc);
354*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
355*49ef7e06SGarrett D'Amore 			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
356*49ef7e06SGarrett D'Amore 			    "bus_disable: ddi_intr_block_disable"
357*49ef7e06SGarrett D'Amore 			    " failed err=%d (table=%p nalloc=%d)",
358*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table, sip->si_nalloc);
359*49ef7e06SGarrett D'Amore 		}
360*49ef7e06SGarrett D'Amore 	} else {
361*49ef7e06SGarrett D'Amore 		index = sip->si_nalloc;
362*49ef7e06SGarrett D'Amore 		while (--index >= 0) {
363*49ef7e06SGarrett D'Amore 			err = ddi_intr_disable(sip->si_table[index]);
364*49ef7e06SGarrett D'Amore 			if (err != DDI_SUCCESS) {
365*49ef7e06SGarrett D'Amore 				dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
366*49ef7e06SGarrett D'Amore 				    "bus_disable: ddi_intr_disable"
367*49ef7e06SGarrett D'Amore 				    " failed err=%d (h=%p idx=%d nalloc=%d)",
368*49ef7e06SGarrett D'Amore 				    err, (void *)sip->si_table[index], index,
369*49ef7e06SGarrett D'Amore 				    sip->si_nalloc);
370*49ef7e06SGarrett D'Amore 			}
371*49ef7e06SGarrett D'Amore 		}
372*49ef7e06SGarrett D'Amore 	}
373*49ef7e06SGarrett D'Amore 
374*49ef7e06SGarrett D'Amore 	sip->si_cap = 0;
375*49ef7e06SGarrett D'Amore 
376*49ef7e06SGarrett D'Amore 	/* Remove all handlers */
377*49ef7e06SGarrett D'Amore 	index = sip->si_nalloc;
378*49ef7e06SGarrett D'Amore 	while (--index >= 0) {
379*49ef7e06SGarrett D'Amore 		err = ddi_intr_remove_handler(sip->si_table[index]);
380*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
381*49ef7e06SGarrett D'Amore 			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
382*49ef7e06SGarrett D'Amore 			    "bus_disable: ddi_intr_remove_handler"
383*49ef7e06SGarrett D'Amore 			    " failed err=%d (h=%p idx=%d nalloc=%d)",
384*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table[index], index,
385*49ef7e06SGarrett D'Amore 			    sip->si_nalloc);
386*49ef7e06SGarrett D'Amore 		}
387*49ef7e06SGarrett D'Amore 	}
388*49ef7e06SGarrett D'Amore 
389*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
390*49ef7e06SGarrett D'Amore }
391*49ef7e06SGarrett D'Amore 
392*49ef7e06SGarrett D'Amore static int
sfxge_intr_nic_enable(sfxge_t * sp)393*49ef7e06SGarrett D'Amore sfxge_intr_nic_enable(sfxge_t *sp)
394*49ef7e06SGarrett D'Amore {
395*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
396*49ef7e06SGarrett D'Amore 	efsys_mem_t *esmp = &(sip->si_mem);
397*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
398*49ef7e06SGarrett D'Amore 	unsigned int index;
399*49ef7e06SGarrett D'Amore 	uint64_t mask;
400*49ef7e06SGarrett D'Amore 	unsigned int count;
401*49ef7e06SGarrett D'Amore 	int rc;
402*49ef7e06SGarrett D'Amore 
403*49ef7e06SGarrett D'Amore 	/* Zero the memory */
404*49ef7e06SGarrett D'Amore 	bzero(esmp->esm_base, EFX_INTR_SIZE);
405*49ef7e06SGarrett D'Amore 
406*49ef7e06SGarrett D'Amore 	/* Enable interrupts at the NIC */
407*49ef7e06SGarrett D'Amore 	if ((rc = efx_intr_init(enp, sip->si_type, esmp)) != 0)
408*49ef7e06SGarrett D'Amore 		goto fail1;
409*49ef7e06SGarrett D'Amore 
410*49ef7e06SGarrett D'Amore 	efx_intr_enable(enp);
411*49ef7e06SGarrett D'Amore 
412*49ef7e06SGarrett D'Amore 	/* FIXME FIXME FIXME */
413*49ef7e06SGarrett D'Amore 	if (sp->s_family == EFX_FAMILY_HUNTINGTON) {
414*49ef7e06SGarrett D'Amore 		/* Disable interrupt test until supported on Huntington. */
415*49ef7e06SGarrett D'Amore 		return (0);
416*49ef7e06SGarrett D'Amore 	}
417*49ef7e06SGarrett D'Amore 	/* FIXME FIXME FIXME */
418*49ef7e06SGarrett D'Amore 
419*49ef7e06SGarrett D'Amore 	/* Test the interrupts */
420*49ef7e06SGarrett D'Amore 	mask = 0;
421*49ef7e06SGarrett D'Amore 	for (index = 0; index < sip->si_nalloc; index++) {
422*49ef7e06SGarrett D'Amore 		mask |= (1 << index);
423*49ef7e06SGarrett D'Amore 
424*49ef7e06SGarrett D'Amore 		rc = efx_intr_trigger(enp, index);
425*49ef7e06SGarrett D'Amore 		ASSERT3U(rc, ==, 0);
426*49ef7e06SGarrett D'Amore 	}
427*49ef7e06SGarrett D'Amore 
428*49ef7e06SGarrett D'Amore 	/* Wait for the tests to complete */
429*49ef7e06SGarrett D'Amore 	count = 0;
430*49ef7e06SGarrett D'Amore 	do {
431*49ef7e06SGarrett D'Amore 		DTRACE_PROBE1(wait, unsigned int, count);
432*49ef7e06SGarrett D'Amore 
433*49ef7e06SGarrett D'Amore 		/* Spin for 1 ms */
434*49ef7e06SGarrett D'Amore 		drv_usecwait(1000);
435*49ef7e06SGarrett D'Amore 
436*49ef7e06SGarrett D'Amore 		/*
437*49ef7e06SGarrett D'Amore 		 * Check to see that all the test interrupts have been
438*49ef7e06SGarrett D'Amore 		 * processed.
439*49ef7e06SGarrett D'Amore 		 */
440*49ef7e06SGarrett D'Amore 		if ((mask & sip->si_mask) == mask)
441*49ef7e06SGarrett D'Amore 			goto done;
442*49ef7e06SGarrett D'Amore 
443*49ef7e06SGarrett D'Amore 	} while (++count < 20);
444*49ef7e06SGarrett D'Amore 
445*49ef7e06SGarrett D'Amore 	rc = ETIMEDOUT;
446*49ef7e06SGarrett D'Amore 	goto fail2;
447*49ef7e06SGarrett D'Amore 
448*49ef7e06SGarrett D'Amore done:
449*49ef7e06SGarrett D'Amore 	return (0);
450*49ef7e06SGarrett D'Amore 
451*49ef7e06SGarrett D'Amore fail2:
452*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
453*49ef7e06SGarrett D'Amore 
454*49ef7e06SGarrett D'Amore 	dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
455*49ef7e06SGarrett D'Amore 	    "Interrupt test failed (mask=%"PRIx64" got=%"
456*49ef7e06SGarrett D'Amore 	    PRIx64"). NIC is disabled",
457*49ef7e06SGarrett D'Amore 	    mask, sip->si_mask);
458*49ef7e06SGarrett D'Amore 
459*49ef7e06SGarrett D'Amore 	DTRACE_PROBE2(int_test_fail, uint64_t, mask, uint64_t, sip->si_mask);
460*49ef7e06SGarrett D'Amore 
461*49ef7e06SGarrett D'Amore 	sip->si_mask = 0;
462*49ef7e06SGarrett D'Amore 
463*49ef7e06SGarrett D'Amore 	/* Disable interrupts at the NIC */
464*49ef7e06SGarrett D'Amore 	efx_intr_disable(enp);
465*49ef7e06SGarrett D'Amore 	efx_intr_fini(enp);
466*49ef7e06SGarrett D'Amore 
467*49ef7e06SGarrett D'Amore fail1:
468*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
469*49ef7e06SGarrett D'Amore 
470*49ef7e06SGarrett D'Amore 	return (rc);
471*49ef7e06SGarrett D'Amore }
472*49ef7e06SGarrett D'Amore 
473*49ef7e06SGarrett D'Amore static void
sfxge_intr_nic_disable(sfxge_t * sp)474*49ef7e06SGarrett D'Amore sfxge_intr_nic_disable(sfxge_t *sp)
475*49ef7e06SGarrett D'Amore {
476*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
477*49ef7e06SGarrett D'Amore 	efx_nic_t *enp = sp->s_enp;
478*49ef7e06SGarrett D'Amore 
479*49ef7e06SGarrett D'Amore 	sip->si_mask = 0;
480*49ef7e06SGarrett D'Amore 
481*49ef7e06SGarrett D'Amore 	/* Disable interrupts at the NIC */
482*49ef7e06SGarrett D'Amore 	efx_intr_disable(enp);
483*49ef7e06SGarrett D'Amore 	efx_intr_fini(enp);
484*49ef7e06SGarrett D'Amore }
485*49ef7e06SGarrett D'Amore 
486*49ef7e06SGarrett D'Amore static inline unsigned
pow2_le(unsigned long n)487*49ef7e06SGarrett D'Amore pow2_le(unsigned long n)
488*49ef7e06SGarrett D'Amore {
489*49ef7e06SGarrett D'Amore 	unsigned int order = 1;
490*49ef7e06SGarrett D'Amore 	ASSERT3U(n, >, 0);
491*49ef7e06SGarrett D'Amore 	while ((1ul << order) <= n) ++order;
492*49ef7e06SGarrett D'Amore 	return (1ul << (order - 1));
493*49ef7e06SGarrett D'Amore }
494*49ef7e06SGarrett D'Amore 
495*49ef7e06SGarrett D'Amore int
sfxge_intr_init(sfxge_t * sp)496*49ef7e06SGarrett D'Amore sfxge_intr_init(sfxge_t *sp)
497*49ef7e06SGarrett D'Amore {
498*49ef7e06SGarrett D'Amore 	dev_info_t *dip = sp->s_dip;
499*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
500*49ef7e06SGarrett D'Amore 	efsys_mem_t *esmp = &(sip->si_mem);
501*49ef7e06SGarrett D'Amore 	sfxge_dma_buffer_attr_t dma_attr;
502*49ef7e06SGarrett D'Amore 	int err;
503*49ef7e06SGarrett D'Amore 	int rc;
504*49ef7e06SGarrett D'Amore 	int types;
505*49ef7e06SGarrett D'Amore 	int type;
506*49ef7e06SGarrett D'Amore 	int index;
507*49ef7e06SGarrett D'Amore 	unsigned int nalloc;
508*49ef7e06SGarrett D'Amore 	int navail;
509*49ef7e06SGarrett D'Amore 
510*49ef7e06SGarrett D'Amore 	SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
511*49ef7e06SGarrett D'Amore 
512*49ef7e06SGarrett D'Amore 	ASSERT3U(sip->si_state, ==, SFXGE_INTR_UNINITIALIZED);
513*49ef7e06SGarrett D'Amore 
514*49ef7e06SGarrett D'Amore #ifdef __sparc
515*49ef7e06SGarrett D'Amore 	/* PSARC 2007/453 */
516*49ef7e06SGarrett D'Amore 	(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
517*49ef7e06SGarrett D'Amore 	    "#msix-request", NULL, 0);
518*49ef7e06SGarrett D'Amore #endif
519*49ef7e06SGarrett D'Amore 
520*49ef7e06SGarrett D'Amore 	/* Get the map of supported interrupt types */
521*49ef7e06SGarrett D'Amore 	err = ddi_intr_get_supported_types(dip, &types);
522*49ef7e06SGarrett D'Amore 	if (err != DDI_SUCCESS) {
523*49ef7e06SGarrett D'Amore 		dev_err(dip, CE_WARN, SFXGE_CMN_ERR
524*49ef7e06SGarrett D'Amore 		    "intr_init: ddi_intr_get_supported_types failed err=%d",
525*49ef7e06SGarrett D'Amore 		    err);
526*49ef7e06SGarrett D'Amore 
527*49ef7e06SGarrett D'Amore 		if (err == DDI_EINVAL)
528*49ef7e06SGarrett D'Amore 			rc = EINVAL;
529*49ef7e06SGarrett D'Amore 		else if (err == DDI_INTR_NOTFOUND)
530*49ef7e06SGarrett D'Amore 			rc = ENOENT;
531*49ef7e06SGarrett D'Amore 		else
532*49ef7e06SGarrett D'Amore 			rc = EFAULT;
533*49ef7e06SGarrett D'Amore 
534*49ef7e06SGarrett D'Amore 		goto fail1;
535*49ef7e06SGarrett D'Amore 	}
536*49ef7e06SGarrett D'Amore 
537*49ef7e06SGarrett D'Amore 	/* Choose most favourable type */
538*49ef7e06SGarrett D'Amore 	if (types & DDI_INTR_TYPE_MSIX) {
539*49ef7e06SGarrett D'Amore 		DTRACE_PROBE(msix);
540*49ef7e06SGarrett D'Amore 
541*49ef7e06SGarrett D'Amore 		type = DDI_INTR_TYPE_MSIX;
542*49ef7e06SGarrett D'Amore 		sip->si_type = EFX_INTR_MESSAGE;
543*49ef7e06SGarrett D'Amore 	} else {
544*49ef7e06SGarrett D'Amore 		DTRACE_PROBE(fixed);
545*49ef7e06SGarrett D'Amore 
546*49ef7e06SGarrett D'Amore 		ASSERT(types & DDI_INTR_TYPE_FIXED);
547*49ef7e06SGarrett D'Amore 
548*49ef7e06SGarrett D'Amore 		type = DDI_INTR_TYPE_FIXED;
549*49ef7e06SGarrett D'Amore 		sip->si_type = EFX_INTR_LINE;
550*49ef7e06SGarrett D'Amore 	}
551*49ef7e06SGarrett D'Amore 
552*49ef7e06SGarrett D'Amore 	/* Get the number of available interrupts */
553*49ef7e06SGarrett D'Amore 	navail = 0;
554*49ef7e06SGarrett D'Amore 	err = ddi_intr_get_navail(dip, type, &navail);
555*49ef7e06SGarrett D'Amore 	if (err != DDI_SUCCESS) {
556*49ef7e06SGarrett D'Amore 		dev_err(dip, CE_WARN, SFXGE_CMN_ERR
557*49ef7e06SGarrett D'Amore 		    "intr_init: ddi_intr_get_navail failed err=%d", err);
558*49ef7e06SGarrett D'Amore 
559*49ef7e06SGarrett D'Amore 		if (err == DDI_EINVAL)
560*49ef7e06SGarrett D'Amore 			rc = EINVAL;
561*49ef7e06SGarrett D'Amore 		else if (err == DDI_INTR_NOTFOUND)
562*49ef7e06SGarrett D'Amore 			rc = ENOENT;
563*49ef7e06SGarrett D'Amore 		else
564*49ef7e06SGarrett D'Amore 			rc = EFAULT;
565*49ef7e06SGarrett D'Amore 
566*49ef7e06SGarrett D'Amore 		goto fail2;
567*49ef7e06SGarrett D'Amore 	}
568*49ef7e06SGarrett D'Amore 
569*49ef7e06SGarrett D'Amore 	/* Double-check */
570*49ef7e06SGarrett D'Amore 	if (navail == 0) {
571*49ef7e06SGarrett D'Amore 		rc = ENOENT;
572*49ef7e06SGarrett D'Amore 		goto fail2;
573*49ef7e06SGarrett D'Amore 	}
574*49ef7e06SGarrett D'Amore 
575*49ef7e06SGarrett D'Amore 	/*
576*49ef7e06SGarrett D'Amore 	 * Allow greater number of MSI-X interrupts than CPUs.
577*49ef7e06SGarrett D'Amore 	 * This can be useful to prevent RX no desc drops; See task 32179.
578*49ef7e06SGarrett D'Amore 	 * Limit non MSI-X interrupts to a single instance.
579*49ef7e06SGarrett D'Amore 	 */
580*49ef7e06SGarrett D'Amore 	if (type != DDI_INTR_TYPE_MSIX)
581*49ef7e06SGarrett D'Amore 		navail = 1;
582*49ef7e06SGarrett D'Amore 	else
583*49ef7e06SGarrett D'Amore 		navail = min(navail, sfxge_rx_scale_prop_get(sp));
584*49ef7e06SGarrett D'Amore 
585*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(navail, unsigned int, navail);
586*49ef7e06SGarrett D'Amore 
587*49ef7e06SGarrett D'Amore 	/* Allocate a handle table */
588*49ef7e06SGarrett D'Amore 	sip->si_table_size = navail * sizeof (ddi_intr_handle_t);
589*49ef7e06SGarrett D'Amore 	sip->si_table = kmem_zalloc(sip->si_table_size, KM_SLEEP);
590*49ef7e06SGarrett D'Amore 
591*49ef7e06SGarrett D'Amore 	/*
592*49ef7e06SGarrett D'Amore 	 * Allocate interrupt handles.
593*49ef7e06SGarrett D'Amore 	 * Serialise all device instances to avoid problems seen in bug31184.
594*49ef7e06SGarrett D'Amore 	 */
595*49ef7e06SGarrett D'Amore 	mutex_enter(&sfxge_global_lock);
596*49ef7e06SGarrett D'Amore 
597*49ef7e06SGarrett D'Amore 	err = ddi_intr_alloc(dip, sip->si_table, type, 0,
598*49ef7e06SGarrett D'Amore 	    navail, &(sip->si_nalloc), DDI_INTR_ALLOC_NORMAL);
599*49ef7e06SGarrett D'Amore 
600*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
601*49ef7e06SGarrett D'Amore 
602*49ef7e06SGarrett D'Amore 	if (err != DDI_SUCCESS) {
603*49ef7e06SGarrett D'Amore 		dev_err(dip, CE_WARN, SFXGE_CMN_ERR
604*49ef7e06SGarrett D'Amore 		    "intr_init: ddi_intr_alloc failed err=%d"
605*49ef7e06SGarrett D'Amore 		    " (navail=%d nalloc=%d)",
606*49ef7e06SGarrett D'Amore 		    err, navail, sip->si_nalloc);
607*49ef7e06SGarrett D'Amore 
608*49ef7e06SGarrett D'Amore 		if (err == DDI_EINVAL)
609*49ef7e06SGarrett D'Amore 			rc = EINVAL;
610*49ef7e06SGarrett D'Amore 		else if (err == DDI_EAGAIN)
611*49ef7e06SGarrett D'Amore 			rc = EAGAIN;
612*49ef7e06SGarrett D'Amore 		else if (err == DDI_INTR_NOTFOUND)
613*49ef7e06SGarrett D'Amore 			rc = ENOENT;
614*49ef7e06SGarrett D'Amore 		else
615*49ef7e06SGarrett D'Amore 			rc = EFAULT;
616*49ef7e06SGarrett D'Amore 
617*49ef7e06SGarrett D'Amore 		goto fail3;
618*49ef7e06SGarrett D'Amore 	}
619*49ef7e06SGarrett D'Amore 
620*49ef7e06SGarrett D'Amore 	/* Double-check */
621*49ef7e06SGarrett D'Amore 	if (sip->si_nalloc == 0) {
622*49ef7e06SGarrett D'Amore 		rc = ENOENT;
623*49ef7e06SGarrett D'Amore 		goto fail3;
624*49ef7e06SGarrett D'Amore 	}
625*49ef7e06SGarrett D'Amore 
626*49ef7e06SGarrett D'Amore 	/* Round down to a power of 2 */
627*49ef7e06SGarrett D'Amore 	nalloc = pow2_le(sip->si_nalloc);
628*49ef7e06SGarrett D'Amore 
629*49ef7e06SGarrett D'Amore 	/* Free off any excess handles */
630*49ef7e06SGarrett D'Amore 	mutex_enter(&sfxge_global_lock);
631*49ef7e06SGarrett D'Amore 
632*49ef7e06SGarrett D'Amore 	index = sip->si_nalloc;
633*49ef7e06SGarrett D'Amore 	while (--index >= nalloc) {
634*49ef7e06SGarrett D'Amore 		(void) ddi_intr_free(sip->si_table[index]);
635*49ef7e06SGarrett D'Amore 		sip->si_table[index] = NULL;
636*49ef7e06SGarrett D'Amore 	}
637*49ef7e06SGarrett D'Amore 
638*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
639*49ef7e06SGarrett D'Amore 
640*49ef7e06SGarrett D'Amore 	sip->si_nalloc = nalloc;
641*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(nalloc, unsigned int, sip->si_nalloc);
642*49ef7e06SGarrett D'Amore 
643*49ef7e06SGarrett D'Amore 	dma_attr.sdba_dip	 = sp->s_dip;
644*49ef7e06SGarrett D'Amore 	dma_attr.sdba_dattrp	 = &sfxge_intr_dma_attr;
645*49ef7e06SGarrett D'Amore 	dma_attr.sdba_callback	 = DDI_DMA_SLEEP;
646*49ef7e06SGarrett D'Amore 	dma_attr.sdba_length	 = EFX_INTR_SIZE;
647*49ef7e06SGarrett D'Amore 	dma_attr.sdba_memflags	 = DDI_DMA_CONSISTENT;
648*49ef7e06SGarrett D'Amore 	dma_attr.sdba_devaccp	 = &sfxge_intr_devacc;
649*49ef7e06SGarrett D'Amore 	dma_attr.sdba_bindflags	 = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
650*49ef7e06SGarrett D'Amore 	dma_attr.sdba_maxcookies = 1;
651*49ef7e06SGarrett D'Amore 	dma_attr.sdba_zeroinit	 = B_TRUE;
652*49ef7e06SGarrett D'Amore 
653*49ef7e06SGarrett D'Amore 	if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
654*49ef7e06SGarrett D'Amore 		goto fail4;
655*49ef7e06SGarrett D'Amore 
656*49ef7e06SGarrett D'Amore 	/* Store the highest priority for convenience */
657*49ef7e06SGarrett D'Amore 	sip->si_intr_pri = 0;
658*49ef7e06SGarrett D'Amore 	for (index = 0; index < sip->si_nalloc; index++) {
659*49ef7e06SGarrett D'Amore 		uint_t pri;
660*49ef7e06SGarrett D'Amore 		if ((rc = ddi_intr_get_pri(sip->si_table[index], &pri)) !=  0)
661*49ef7e06SGarrett D'Amore 			goto fail5;
662*49ef7e06SGarrett D'Amore 		if (pri > sip->si_intr_pri)
663*49ef7e06SGarrett D'Amore 			sip->si_intr_pri = pri;
664*49ef7e06SGarrett D'Amore 	}
665*49ef7e06SGarrett D'Amore 
666*49ef7e06SGarrett D'Amore 	sip->si_state = SFXGE_INTR_INITIALIZED;
667*49ef7e06SGarrett D'Amore 	return (0);
668*49ef7e06SGarrett D'Amore 
669*49ef7e06SGarrett D'Amore fail5:
670*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail5);
671*49ef7e06SGarrett D'Amore 
672*49ef7e06SGarrett D'Amore fail4:
673*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail4);
674*49ef7e06SGarrett D'Amore 
675*49ef7e06SGarrett D'Amore 	/* Free interrupt handles */
676*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
677*49ef7e06SGarrett D'Amore 
678*49ef7e06SGarrett D'Amore 	index = sip->si_nalloc;
679*49ef7e06SGarrett D'Amore 	while (--index >= 0) {
680*49ef7e06SGarrett D'Amore 		err = ddi_intr_free(sip->si_table[index]);
681*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
682*49ef7e06SGarrett D'Amore 			dev_err(dip, CE_WARN, SFXGE_CMN_ERR
683*49ef7e06SGarrett D'Amore 			    "intr_init: ddi_intr_free failed err=%d"
684*49ef7e06SGarrett D'Amore 			    " (h=%p idx=%d nalloc=%d)",
685*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table[index], index,
686*49ef7e06SGarrett D'Amore 			    sip->si_nalloc);
687*49ef7e06SGarrett D'Amore 		}
688*49ef7e06SGarrett D'Amore 		sip->si_table[index] = NULL;
689*49ef7e06SGarrett D'Amore 	}
690*49ef7e06SGarrett D'Amore 	sip->si_nalloc = 0;
691*49ef7e06SGarrett D'Amore 
692*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
693*49ef7e06SGarrett D'Amore 
694*49ef7e06SGarrett D'Amore fail3:
695*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail3);
696*49ef7e06SGarrett D'Amore 
697*49ef7e06SGarrett D'Amore 	/* Free the handle table */
698*49ef7e06SGarrett D'Amore 	kmem_free(sip->si_table, sip->si_table_size);
699*49ef7e06SGarrett D'Amore 	sip->si_table = NULL;
700*49ef7e06SGarrett D'Amore 	sip->si_table_size = 0;
701*49ef7e06SGarrett D'Amore 
702*49ef7e06SGarrett D'Amore fail2:
703*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
704*49ef7e06SGarrett D'Amore 
705*49ef7e06SGarrett D'Amore 	/* Clear the interrupt type */
706*49ef7e06SGarrett D'Amore 	sip->si_type = EFX_INTR_INVALID;
707*49ef7e06SGarrett D'Amore 
708*49ef7e06SGarrett D'Amore fail1:
709*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
710*49ef7e06SGarrett D'Amore 
711*49ef7e06SGarrett D'Amore 	SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
712*49ef7e06SGarrett D'Amore 
713*49ef7e06SGarrett D'Amore 	return (rc);
714*49ef7e06SGarrett D'Amore }
715*49ef7e06SGarrett D'Amore 
716*49ef7e06SGarrett D'Amore int
sfxge_intr_start(sfxge_t * sp)717*49ef7e06SGarrett D'Amore sfxge_intr_start(sfxge_t *sp)
718*49ef7e06SGarrett D'Amore {
719*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
720*49ef7e06SGarrett D'Amore 	int rc;
721*49ef7e06SGarrett D'Amore 
722*49ef7e06SGarrett D'Amore 	ASSERT3U(sip->si_state, ==, SFXGE_INTR_INITIALIZED);
723*49ef7e06SGarrett D'Amore 
724*49ef7e06SGarrett D'Amore 	/* Enable interrupts at the bus */
725*49ef7e06SGarrett D'Amore 	if ((rc = sfxge_intr_bus_enable(sp)) != 0)
726*49ef7e06SGarrett D'Amore 		goto fail1;
727*49ef7e06SGarrett D'Amore 
728*49ef7e06SGarrett D'Amore 	sip->si_state = SFXGE_INTR_TESTING;
729*49ef7e06SGarrett D'Amore 
730*49ef7e06SGarrett D'Amore 	/* Enable interrupts at the NIC */
731*49ef7e06SGarrett D'Amore 	if ((rc = sfxge_intr_nic_enable(sp)) != 0)
732*49ef7e06SGarrett D'Amore 		goto fail2;
733*49ef7e06SGarrett D'Amore 
734*49ef7e06SGarrett D'Amore 	sip->si_state = SFXGE_INTR_STARTED;
735*49ef7e06SGarrett D'Amore 
736*49ef7e06SGarrett D'Amore 	return (0);
737*49ef7e06SGarrett D'Amore 
738*49ef7e06SGarrett D'Amore fail2:
739*49ef7e06SGarrett D'Amore 	DTRACE_PROBE(fail2);
740*49ef7e06SGarrett D'Amore 
741*49ef7e06SGarrett D'Amore 	/* Disable interrupts at the bus */
742*49ef7e06SGarrett D'Amore 	sfxge_intr_bus_disable(sp);
743*49ef7e06SGarrett D'Amore 
744*49ef7e06SGarrett D'Amore fail1:
745*49ef7e06SGarrett D'Amore 	DTRACE_PROBE1(fail1, int, rc);
746*49ef7e06SGarrett D'Amore 
747*49ef7e06SGarrett D'Amore 	sip->si_state = SFXGE_INTR_INITIALIZED;
748*49ef7e06SGarrett D'Amore 
749*49ef7e06SGarrett D'Amore 	return (rc);
750*49ef7e06SGarrett D'Amore }
751*49ef7e06SGarrett D'Amore 
752*49ef7e06SGarrett D'Amore void
sfxge_intr_stop(sfxge_t * sp)753*49ef7e06SGarrett D'Amore sfxge_intr_stop(sfxge_t *sp)
754*49ef7e06SGarrett D'Amore {
755*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
756*49ef7e06SGarrett D'Amore 
757*49ef7e06SGarrett D'Amore 	ASSERT3U(sip->si_state, ==, SFXGE_INTR_STARTED);
758*49ef7e06SGarrett D'Amore 
759*49ef7e06SGarrett D'Amore 	sip->si_state = SFXGE_INTR_INITIALIZED;
760*49ef7e06SGarrett D'Amore 
761*49ef7e06SGarrett D'Amore 	/* Disable interrupts at the NIC */
762*49ef7e06SGarrett D'Amore 	sfxge_intr_nic_disable(sp);
763*49ef7e06SGarrett D'Amore 
764*49ef7e06SGarrett D'Amore 	/* Disable interrupts at the bus */
765*49ef7e06SGarrett D'Amore 	sfxge_intr_bus_disable(sp);
766*49ef7e06SGarrett D'Amore }
767*49ef7e06SGarrett D'Amore 
768*49ef7e06SGarrett D'Amore void
sfxge_intr_fini(sfxge_t * sp)769*49ef7e06SGarrett D'Amore sfxge_intr_fini(sfxge_t *sp)
770*49ef7e06SGarrett D'Amore {
771*49ef7e06SGarrett D'Amore 	sfxge_intr_t *sip = &(sp->s_intr);
772*49ef7e06SGarrett D'Amore 	efsys_mem_t *esmp = &(sip->si_mem);
773*49ef7e06SGarrett D'Amore 	int index;
774*49ef7e06SGarrett D'Amore 	int err;
775*49ef7e06SGarrett D'Amore 
776*49ef7e06SGarrett D'Amore 	ASSERT3U(sip->si_state, ==, SFXGE_INTR_INITIALIZED);
777*49ef7e06SGarrett D'Amore 
778*49ef7e06SGarrett D'Amore 	sip->si_state = SFXGE_INTR_UNINITIALIZED;
779*49ef7e06SGarrett D'Amore 
780*49ef7e06SGarrett D'Amore 	/* Tear down dma setup */
781*49ef7e06SGarrett D'Amore 	sfxge_dma_buffer_destroy(esmp);
782*49ef7e06SGarrett D'Amore 
783*49ef7e06SGarrett D'Amore 
784*49ef7e06SGarrett D'Amore 	/* Free interrupt handles */
785*49ef7e06SGarrett D'Amore 	mutex_enter(&sfxge_global_lock);
786*49ef7e06SGarrett D'Amore 
787*49ef7e06SGarrett D'Amore 	index = sip->si_nalloc;
788*49ef7e06SGarrett D'Amore 	while (--index >= 0) {
789*49ef7e06SGarrett D'Amore 		err = ddi_intr_free(sip->si_table[index]);
790*49ef7e06SGarrett D'Amore 		if (err != DDI_SUCCESS) {
791*49ef7e06SGarrett D'Amore 			dev_err(sp->s_dip, CE_WARN, SFXGE_CMN_ERR
792*49ef7e06SGarrett D'Amore 			    "intr_fini: ddi_intr_free failed err=%d"
793*49ef7e06SGarrett D'Amore 			    " (h=%p idx=%d nalloc=%d)",
794*49ef7e06SGarrett D'Amore 			    err, (void *)sip->si_table[index],
795*49ef7e06SGarrett D'Amore 			    index, sip->si_nalloc);
796*49ef7e06SGarrett D'Amore 		}
797*49ef7e06SGarrett D'Amore 		sip->si_table[index] = NULL;
798*49ef7e06SGarrett D'Amore 	}
799*49ef7e06SGarrett D'Amore 	sip->si_nalloc = 0;
800*49ef7e06SGarrett D'Amore 
801*49ef7e06SGarrett D'Amore 	mutex_exit(&sfxge_global_lock);
802*49ef7e06SGarrett D'Amore 
803*49ef7e06SGarrett D'Amore 	/* Free the handle table */
804*49ef7e06SGarrett D'Amore 	kmem_free(sip->si_table, sip->si_table_size);
805*49ef7e06SGarrett D'Amore 	sip->si_table = NULL;
806*49ef7e06SGarrett D'Amore 	sip->si_table_size = 0;
807*49ef7e06SGarrett D'Amore 
808*49ef7e06SGarrett D'Amore 	/* Clear the interrupt type */
809*49ef7e06SGarrett D'Amore 	sip->si_type = EFX_INTR_INVALID;
810*49ef7e06SGarrett D'Amore 
811*49ef7e06SGarrett D'Amore 	SFXGE_OBJ_CHECK(sip, sfxge_intr_t);
812*49ef7e06SGarrett D'Amore }
813