1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /* Copyright © 2003-2011 Emulex. All rights reserved.  */
23 
24 /*
25  * Source file interrupt registration
26  * and related helper functions
27  */
28 
29 #include <oce_impl.h>
30 
31 
32 static uint_t oce_isr(caddr_t arg1, caddr_t arg2);
33 
34 /*
35  * top level function to setup interrupts
36  *
37  * dev - software handle to the device
38  *
39  * return DDI_SUCCESS => success, failure otherwise
40  */
41 int
oce_setup_intr(struct oce_dev * dev)42 oce_setup_intr(struct oce_dev *dev)
43 {
44 	int ret;
45 	int intr_types = 0;
46 	int navail = 0;
47 	int nsupported = 0;
48 	int min = 0;
49 	int nreqd = 0;
50 	int nallocd = 0;
51 
52 	/* get supported intr types */
53 	ret = ddi_intr_get_supported_types(dev->dip, &intr_types);
54 	if (ret != DDI_SUCCESS) {
55 		oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
56 		    "Failed to retrieve intr types ");
57 		return (DDI_FAILURE);
58 	}
59 
60 retry_intr:
61 	if (intr_types & DDI_INTR_TYPE_MSIX) {
62 		dev->intr_type = DDI_INTR_TYPE_MSIX;
63 		/* one vector is shared by MCC and Tx */
64 		nreqd = dev->rx_rings + 1;
65 		min = OCE_MIN_VECTORS;
66 	} else if (intr_types & DDI_INTR_TYPE_FIXED) {
67 		dev->intr_type = DDI_INTR_TYPE_FIXED;
68 		nreqd = OCE_MIN_VECTORS;
69 		min = OCE_MIN_VECTORS;
70 	}
71 
72 	ret = ddi_intr_get_nintrs(dev->dip, dev->intr_type, &nsupported);
73 	if (ret != DDI_SUCCESS) {
74 		oce_log(dev, CE_WARN, MOD_CONFIG,
75 		    "Could not get nintrs:0x%d", ret);
76 		return (DDI_FAILURE);
77 	}
78 
79 	/* get the number of vectors available */
80 	ret = ddi_intr_get_navail(dev->dip, dev->intr_type, &navail);
81 	if (ret != DDI_SUCCESS || navail < min) {
82 		oce_log(dev, CE_WARN, MOD_CONFIG,
83 		    "Could not get msix vectors:0x%x",
84 		    navail);
85 		return (DDI_FAILURE);
86 	}
87 
88 	if (navail < min) {
89 		return (DDI_FAILURE);
90 	}
91 
92 	/* if the requested number is more than available reset reqd */
93 	if (navail < nreqd) {
94 		nreqd = navail;
95 	}
96 
97 	/* allocate htable */
98 	dev->hsize  = nreqd *  sizeof (ddi_intr_handle_t);
99 	dev->htable = kmem_zalloc(dev->hsize,  KM_NOSLEEP);
100 
101 	if (dev->htable == NULL)
102 		return (DDI_FAILURE);
103 
104 	nallocd = 0;
105 	/* allocate interrupt handlers */
106 	ret = ddi_intr_alloc(dev->dip, dev->htable, dev->intr_type,
107 	    0, nreqd, &nallocd, DDI_INTR_ALLOC_NORMAL);
108 
109 	if (ret != DDI_SUCCESS) {
110 		goto fail_intr;
111 	}
112 
113 	dev->num_vectors = nallocd;
114 	if (nallocd < min) {
115 		goto fail_intr;
116 	}
117 
118 	/*
119 	 * get the interrupt priority. Assumption is that all handlers have
120 	 * equal priority
121 	 */
122 
123 	ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri);
124 
125 	if (ret != DDI_SUCCESS) {
126 		goto fail_intr;
127 	}
128 
129 	(void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap);
130 
131 	if ((intr_types & DDI_INTR_TYPE_MSIX) && (nallocd > 1)) {
132 		dev->rx_rings = nallocd - 1;
133 	} else {
134 		dev->rx_rings = 1;
135 	}
136 
137 	return (DDI_SUCCESS);
138 
139 fail_intr:
140 	(void) oce_teardown_intr(dev);
141 	if ((dev->intr_type == DDI_INTR_TYPE_MSIX) &&
142 	    (intr_types & DDI_INTR_TYPE_FIXED)) {
143 		intr_types &= ~DDI_INTR_TYPE_MSIX;
144 		oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
145 		    "Could not get MSIX vectors, trying for FIXED vectors");
146 		goto retry_intr;
147 	}
148 	return (DDI_FAILURE);
149 }
150 
151 /*
152  * top level function to undo initialization in oce_setup_intr
153  *
154  * dev - software handle to the device
155  *
156  * return DDI_SUCCESS => success, failure otherwise
157  */
158 int
oce_teardown_intr(struct oce_dev * dev)159 oce_teardown_intr(struct oce_dev *dev)
160 {
161 	int i;
162 
163 	/* release handlers */
164 	for (i = 0; i < dev->num_vectors; i++) {
165 		(void) ddi_intr_free(dev->htable[i]);
166 	}
167 
168 	/* release htable */
169 	kmem_free(dev->htable, dev->hsize);
170 	dev->htable = NULL;
171 
172 	return (DDI_SUCCESS);
173 }
174 
175 /*
176  * helper function to add ISR based on interrupt type
177  *
178  * dev - software handle to the device
179  *
180  * return DDI_SUCCESS => success, failure otherwise
181  */
182 int
oce_setup_handlers(struct oce_dev * dev)183 oce_setup_handlers(struct oce_dev *dev)
184 {
185 	int i = 0;
186 	int ret;
187 	for (i = 0; i < dev->num_vectors; i++) {
188 		ret = ddi_intr_add_handler(dev->htable[i], oce_isr,
189 		    (caddr_t)dev->eq[i], NULL);
190 		if (ret != DDI_SUCCESS) {
191 			oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
192 			    "Failed to add interrupt handlers");
193 			for (i--; i >= 0; i--) {
194 				(void) ddi_intr_remove_handler(dev->htable[i]);
195 			}
196 			return (DDI_FAILURE);
197 		}
198 	}
199 	return (DDI_SUCCESS);
200 }
201 
202 /*
203  * helper function to remove ISRs added in oce_setup_handlers
204  *
205  * dev - software handle to the device
206  *
207  * return DDI_SUCCESS => success, failure otherwise
208  */
209 void
oce_remove_handler(struct oce_dev * dev)210 oce_remove_handler(struct oce_dev *dev)
211 {
212 	int nvec;
213 	for (nvec = 0; nvec < dev->num_vectors; nvec++) {
214 		(void) ddi_intr_remove_handler(dev->htable[nvec]);
215 	}
216 }
217 
218 void
oce_chip_ei(struct oce_dev * dev)219 oce_chip_ei(struct oce_dev *dev)
220 {
221 	uint32_t reg;
222 
223 	reg =  OCE_CFG_READ32(dev, PCICFG_INTR_CTRL);
224 	reg |= HOSTINTR_MASK;
225 	OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg);
226 }
227 
228 /*
229  * function to enable interrupts
230  *
231  * dev - software handle to the device
232  *
233  * return DDI_SUCCESS => success, failure otherwise
234  */
235 void
oce_ei(struct oce_dev * dev)236 oce_ei(struct oce_dev *dev)
237 {
238 	int i;
239 	int ret;
240 
241 	if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
242 		(void) ddi_intr_block_enable(dev->htable, dev->num_vectors);
243 	} else {
244 
245 		for (i = 0; i < dev->num_vectors; i++) {
246 			ret = ddi_intr_enable(dev->htable[i]);
247 			if (ret != DDI_SUCCESS) {
248 				for (i--; i >= 0; i--) {
249 					(void) ddi_intr_disable(dev->htable[i]);
250 				}
251 			}
252 		}
253 	}
254 	oce_chip_ei(dev);
255 } /* oce_ei */
256 
257 void
oce_chip_di(struct oce_dev * dev)258 oce_chip_di(struct oce_dev *dev)
259 {
260 	uint32_t reg;
261 
262 	reg =  OCE_CFG_READ32(dev, PCICFG_INTR_CTRL);
263 	reg &= ~HOSTINTR_MASK;
264 	OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg);
265 }
266 
267 /*
268  * function to disable interrupts
269  *
270  * dev - software handle to the device
271  *
272  * return DDI_SUCCESS => success, failure otherwise
273  */
274 void
oce_di(struct oce_dev * dev)275 oce_di(struct oce_dev *dev)
276 {
277 	int i;
278 	int ret;
279 
280 	oce_chip_di(dev);
281 	if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
282 		(void) ddi_intr_block_disable(dev->htable, dev->num_vectors);
283 	} else {
284 		for (i = 0; i < dev->num_vectors; i++) {
285 			ret = ddi_intr_disable(dev->htable[i]);
286 			if (ret != DDI_SUCCESS) {
287 				oce_log(dev, CE_WARN, MOD_CONFIG,
288 				    "Failed to disable interrupts 0x%x", ret);
289 			}
290 		}
291 	}
292 
293 } /* oce_di */
294 
295 /*
296  * command interrupt handler routine added to all vectors
297  *
298  * arg1 = callback data
299  * arg2 - callback data
300  *
301  * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR
302  */
303 static uint_t
oce_isr(caddr_t arg1,caddr_t arg2)304 oce_isr(caddr_t arg1, caddr_t arg2)
305 {
306 	struct oce_eq *eq;
307 	struct oce_eqe *eqe;
308 	uint16_t num_eqe = 0;
309 	uint16_t cq_id;
310 	struct oce_cq *cq;
311 	struct oce_dev  *dev;
312 
313 	_NOTE(ARGUNUSED(arg2));
314 
315 	eq = (struct oce_eq *)(void *)(arg1);
316 
317 	dev = eq->parent;
318 
319 	eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
320 
321 	while (eqe->u0.dw0) {
322 
323 		eqe->u0.dw0 = LE_32(eqe->u0.dw0);
324 
325 		/* if not CQ then continue else flag an error */
326 		if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) {
327 			oce_log(dev, CE_WARN, MOD_ISR,
328 			    "NOT a CQ event. 0x%x",
329 			    eqe->u0.s.major_code);
330 		}
331 
332 		/* get the cq from the eqe */
333 		cq_id = eqe->u0.s.resource_id % OCE_MAX_CQ;
334 		cq = dev->cq[cq_id];
335 
336 		/* Call the completion handler */
337 		(void) cq->cq_handler(cq->cb_arg);
338 
339 		/* clear valid bit and progress eqe */
340 		eqe->u0.dw0 = 0;
341 		RING_GET(eq->ring, 1);
342 		eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
343 		num_eqe++;
344 	} /* for all EQEs */
345 
346 	/* ring the eq doorbell, signify that it's done processing  */
347 	oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE);
348 	if (num_eqe > 0) {
349 		return (DDI_INTR_CLAIMED);
350 	} else {
351 		return (DDI_INTR_UNCLAIMED);
352 	}
353 } /* oce_msix_handler */
354