xref: /illumos-gate/usr/src/uts/common/io/fibre-channel/fca/oce/oce_intr.c (revision 56f33205c9ed776c3c909e07d52e94610a675740)
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 /*
23  * Copyright 2009 Emulex.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Source file interrupt registration
29  * and related helper functions
30  */
31 
32 #include <oce_impl.h>
33 
34 static int oce_setup_msix(struct oce_dev *dev);
35 static int oce_teardown_msix(struct oce_dev *dev);
36 static int oce_add_msix_handlers(struct oce_dev *dev);
37 static void oce_del_msix_handlers(struct oce_dev *dev);
38 static uint_t oce_isr(caddr_t arg1, caddr_t arg2);
39 
40 static int oce_setup_intx(struct oce_dev *dev);
41 static int oce_teardown_intx(struct oce_dev *dev);
42 static int oce_add_intx_handlers(struct oce_dev *dev);
43 static void oce_del_intx_handlers(struct oce_dev *dev);
44 
45 /*
46  * top level function to setup interrupts
47  *
48  * dev - software handle to the device
49  *
50  * return DDI_SUCCESS => success, failure otherwise
51  */
52 int
53 oce_setup_intr(struct oce_dev *dev)
54 {
55 	int ret;
56 
57 	if (dev->intr_types & DDI_INTR_TYPE_MSIX) {
58 		ret = oce_setup_msix(dev);
59 		if (ret == 0)
60 			return (ret);
61 	}
62 
63 	if (dev->intr_types & DDI_INTR_TYPE_FIXED) {
64 		ret = oce_setup_intx(dev);
65 		if (ret == 0)
66 			return (ret);
67 	}
68 
69 	return (DDI_FAILURE);
70 }
71 
72 /*
73  * top level function to undo initialization in oce_setup_intr
74  *
75  * dev - software handle to the device
76  *
77  * return DDI_SUCCESS => success, failure otherwise
78  */
79 int
80 oce_teardown_intr(struct oce_dev *dev)
81 {
82 	if (dev->intr_types ==  DDI_INTR_TYPE_MSIX) {
83 		return (oce_teardown_msix(dev));
84 	}
85 
86 	if (dev->intr_types == DDI_INTR_TYPE_FIXED) {
87 		return (oce_teardown_intx(dev));
88 	}
89 
90 	return (DDI_FAILURE);
91 }
92 
93 /*
94  * helper function to add ISR based on interrupt type
95  *
96  * dev - software handle to the device
97  *
98  * return DDI_SUCCESS => success, failure otherwise
99  */
100 int
101 oce_setup_handlers(struct oce_dev *dev)
102 {
103 	if (dev->intr_types == DDI_INTR_TYPE_MSIX) {
104 		return (oce_add_msix_handlers(dev));
105 	}
106 
107 	if (dev->intr_types == DDI_INTR_TYPE_FIXED) {
108 		return (oce_add_intx_handlers(dev));
109 	}
110 
111 	return (DDI_FAILURE);
112 }
113 
114 /*
115  * helper function to remove ISRs added in oce_setup_handlers
116  *
117  * dev - software handle to the device
118  *
119  * return DDI_SUCCESS => success, failure otherwise
120  */
121 void
122 oce_remove_handler(struct oce_dev *dev)
123 {
124 	if (dev->intr_types == DDI_INTR_TYPE_MSIX) {
125 		oce_del_msix_handlers(dev);
126 	}
127 
128 	if (dev->intr_types == DDI_INTR_TYPE_FIXED) {
129 		oce_del_intx_handlers(dev);
130 	}
131 }
132 
133 void
134 oce_chip_ei(struct oce_dev *dev)
135 {
136 	uint32_t reg;
137 
138 	reg =  OCE_CFG_READ32(dev, PCICFG_INTR_CTRL);
139 	reg |= HOSTINTR_MASK;
140 	OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg);
141 }
142 
143 /*
144  * function to enable interrupts
145  *
146  * dev - software handle to the device
147  *
148  * return DDI_SUCCESS => success, failure otherwise
149  */
150 void
151 oce_ei(struct oce_dev *dev)
152 {
153 	int i;
154 	int ret;
155 
156 	if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
157 		(void) ddi_intr_block_enable(dev->htable, dev->num_vectors);
158 	} else {
159 
160 		for (i = 0; i < dev->num_vectors; i++) {
161 			ret = ddi_intr_enable(dev->htable[i]);
162 			if (ret != DDI_SUCCESS) {
163 				for (i--; i >= 0; i--) {
164 					(void) ddi_intr_disable(dev->htable[i]);
165 				}
166 			}
167 		}
168 	}
169 	oce_chip_ei(dev);
170 } /* oce_ei */
171 
172 void
173 oce_chip_di(struct oce_dev *dev)
174 {
175 	uint32_t reg;
176 
177 	reg =  OCE_CFG_READ32(dev, PCICFG_INTR_CTRL);
178 	reg &= ~HOSTINTR_MASK;
179 	OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg);
180 }
181 
182 /*
183  * function to disable interrupts
184  *
185  * dev - software handle to the device
186  *
187  * return DDI_SUCCESS => success, failure otherwise
188  */
189 void
190 oce_di(struct oce_dev *dev)
191 {
192 	int i;
193 	int ret;
194 
195 	oce_chip_di(dev);
196 
197 	if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
198 		(void) ddi_intr_block_disable(dev->htable, dev->num_vectors);
199 	} else {
200 		for (i = 0; i < dev->num_vectors; i++) {
201 			ret = ddi_intr_disable(dev->htable[i]);
202 			if (ret != DDI_SUCCESS) {
203 				oce_log(dev, CE_WARN, MOD_CONFIG,
204 				    "Failed to disable interrupts 0x%x", ret);
205 			}
206 		}
207 	}
208 } /* oce_di */
209 
210 /*
211  * function to setup the MSIX vectors
212  *
213  * dev - software handle to the device
214  *
215  * return 0=>success, failure otherwise
216  */
217 static int
218 oce_setup_msix(struct oce_dev *dev)
219 {
220 	int navail = 0;
221 	int ret = 0;
222 
223 	ret = ddi_intr_get_nintrs(dev->dip, DDI_INTR_TYPE_MSIX, &navail);
224 	if (ret != DDI_SUCCESS) {
225 		oce_log(dev, CE_WARN, MOD_CONFIG,
226 		    "Could not get nintrs:0x%x %d",
227 		    navail, ret);
228 		return (DDI_FAILURE);
229 	}
230 
231 	/* get the number of vectors available */
232 	ret = ddi_intr_get_navail(dev->dip, DDI_INTR_TYPE_MSIX, &navail);
233 	if (ret != DDI_SUCCESS) {
234 		oce_log(dev, CE_WARN, MOD_CONFIG,
235 		    "Could not get msix vectors:0x%x",
236 		    navail);
237 		return (DDI_FAILURE);
238 	}
239 
240 	if (navail < OCE_NUM_USED_VECTORS)
241 		return (DDI_FAILURE);
242 
243 	dev->num_vectors = OCE_NUM_USED_VECTORS;
244 	dev->intr_types = DDI_INTR_TYPE_MSIX;
245 
246 	/* allocate htable */
247 	dev->htable = kmem_zalloc(dev->num_vectors *
248 	    sizeof (ddi_intr_handle_t), KM_SLEEP);
249 
250 	/* allocate interrupt handlers */
251 	ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_MSIX,
252 	    0, dev->num_vectors, &navail, DDI_INTR_ALLOC_NORMAL);
253 
254 	if (ret != DDI_SUCCESS || navail < OCE_NUM_USED_VECTORS) {
255 		oce_log(dev, CE_WARN, MOD_CONFIG,
256 		    "Alloc intr failed: %d %d",
257 		    navail, ret);
258 		kmem_free(dev->htable,
259 		    dev->num_vectors * sizeof (ddi_intr_handle_t));
260 		return (DDI_FAILURE);
261 	}
262 
263 	/* update the actual number of interrupts allocated */
264 	dev->num_vectors = navail;
265 
266 	/*
267 	 * get the interrupt priority. Assumption is that all handlers have
268 	 * equal priority
269 	 */
270 
271 	ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri);
272 
273 	if (ret != DDI_SUCCESS) {
274 		int i;
275 		oce_log(dev, CE_WARN, MOD_CONFIG,
276 		    "Unable to get intr priority: 0x%x",
277 		    dev->intr_pri);
278 		for (i = 0; i < dev->num_vectors; i++) {
279 			(void) ddi_intr_free(dev->htable[i]);
280 		}
281 		kmem_free(dev->htable,
282 		    dev->num_vectors * sizeof (ddi_intr_handle_t));
283 		return (DDI_FAILURE);
284 	}
285 
286 	(void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap);
287 	return (DDI_SUCCESS);
288 } /* oce_setup_msix */
289 
290 /*
291  * helper function to teardown MSIX interrupts
292  *
293  * dev - software handle to the device
294  *
295  * return 0 => success, failure otherwise
296  */
297 static int
298 oce_teardown_msix(struct oce_dev *dev)
299 {
300 	int i;
301 
302 	/* release handlers */
303 	for (i = 0; i < dev->num_vectors; i++) {
304 		(void) ddi_intr_free(dev->htable[i]);
305 	}
306 
307 	/* release htable */
308 	kmem_free(dev->htable,
309 	    dev->num_vectors * sizeof (ddi_intr_handle_t));
310 
311 	return (DDI_SUCCESS);
312 } /* oce_teardown_msix */
313 
314 /*
315  * function to add MSIX handlers to vectors
316  *
317  * dev - software handle to the device
318  *
319  * return DDI_SUCCESS => success, failure otherwise
320  */
321 static int
322 oce_add_msix_handlers(struct oce_dev *dev)
323 {
324 	int ret;
325 	int i;
326 
327 	if (!(dev->intr_types & DDI_INTR_TYPE_MSIX)) {
328 		return (DDI_FAILURE);
329 	}
330 	for (i = 0; i < dev->neqs; i++) {
331 		ret = ddi_intr_add_handler(dev->htable[i], oce_isr,
332 		    (caddr_t)dev->eq[i], NULL);
333 		if (ret != DDI_SUCCESS) {
334 			oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
335 			    "Failed to add interrupt handlers");
336 			for (i--; i >= 0; i--) {
337 				(void) ddi_intr_remove_handler(dev->htable[i]);
338 			}
339 			return (DDI_FAILURE);
340 		}
341 	}
342 
343 	return (DDI_SUCCESS);
344 } /* oce_add_msix_handlers */
345 
346 /*
347  * function to disassociate msix handlers added in oce_add_msix_handlers
348  *
349  * dev - software handle to the device
350  *
351  * return DDI_SUCCESS => success, failure otherwise
352  */
353 static void
354 oce_del_msix_handlers(struct oce_dev *dev)
355 {
356 	int nvec;
357 
358 	for (nvec = 0; nvec < dev->neqs; nvec++) {
359 		(void) ddi_intr_remove_handler(dev->htable[nvec]);
360 	}
361 } /* oce_del_msix_handlers */
362 
363 /*
364  * command interrupt handler routine added to all vectors
365  *
366  * arg1 = callback data
367  * arg2 - callback data
368  *
369  * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR
370  */
371 static uint_t
372 oce_isr(caddr_t arg1, caddr_t arg2)
373 {
374 	struct oce_eq *eq;
375 	struct oce_eqe *eqe;
376 	uint16_t num_eqe = 0;
377 	uint16_t cq_id;
378 	struct oce_cq *cq;
379 	struct oce_dev  *dev;
380 
381 	_NOTE(ARGUNUSED(arg2));
382 
383 	eq = (struct oce_eq *)(void *)(arg1);
384 
385 	if (eq == NULL) {
386 		return (DDI_INTR_UNCLAIMED);
387 	}
388 	dev = eq->parent;
389 
390 	/* If device is getting suspended or closing, then return */
391 	if ((dev == NULL) ||
392 	    (dev->state & STATE_MAC_STOPPING) ||
393 	    !(dev->state & STATE_MAC_STARTED) ||
394 	    dev->suspended) {
395 		return (DDI_INTR_UNCLAIMED);
396 	}
397 
398 	eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
399 
400 	while (eqe->u0.dw0) {
401 
402 		eqe->u0.dw0 = LE_32(eqe->u0.dw0);
403 
404 		/* if not CQ then continue else flag an error */
405 		if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) {
406 			oce_log(dev, CE_WARN, MOD_ISR,
407 			    "NOT a CQ event. 0x%x",
408 			    eqe->u0.s.major_code);
409 		}
410 
411 		/* get the cq from the eqe */
412 		cq_id = eqe->u0.s.resource_id;
413 		cq = dev->cq[cq_id];
414 
415 		/* Call the completion handler */
416 		(void) cq->cq_handler(cq->cb_arg);
417 
418 		/* clear valid bit and progress eqe */
419 		eqe->u0.dw0 = 0;
420 		RING_GET(eq->ring, 1);
421 		eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
422 		num_eqe++;
423 	} /* for all EQEs */
424 
425 	/* ring the eq doorbell, signify that it's done processing  */
426 	if (num_eqe > 0) {
427 		oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE);
428 		return (DDI_INTR_CLAIMED);
429 	} else {
430 		return (DDI_INTR_UNCLAIMED);
431 	}
432 } /* oce_msix_handler */
433 
434 static int
435 oce_setup_intx(struct oce_dev *dev)
436 {
437 	int navail = 0;
438 	int nintr = 0;
439 	int ret = 0;
440 
441 	ret = ddi_intr_get_nintrs(dev->dip, DDI_INTR_TYPE_FIXED, &nintr);
442 	if (ret != DDI_SUCCESS) {
443 		oce_log(dev, CE_WARN, MOD_CONFIG,
444 		    "could not get nintrs:0x%x %d",
445 		    navail, ret);
446 		return (DDI_FAILURE);
447 	}
448 
449 	/* get the number of vectors available */
450 	ret = ddi_intr_get_navail(dev->dip, DDI_INTR_TYPE_FIXED, &navail);
451 	if (ret != DDI_SUCCESS) {
452 		oce_log(dev, CE_WARN, MOD_CONFIG,
453 		    "could not get intx vectors:0x%x",
454 		    navail);
455 		return (DDI_FAILURE);
456 	}
457 
458 	/* always 1 */
459 	if (navail != nintr)
460 		return (DDI_FAILURE);
461 
462 	dev->num_vectors = navail;
463 	dev->intr_types = DDI_INTR_TYPE_FIXED;
464 
465 	/* allocate htable */
466 	dev->htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
467 
468 	/* allocate interrupt handlers */
469 	ret = ddi_intr_alloc(dev->dip, dev->htable, DDI_INTR_TYPE_FIXED,
470 	    0, dev->num_vectors, &navail, DDI_INTR_ALLOC_NORMAL);
471 
472 	if (ret != DDI_SUCCESS || navail != 1) {
473 		oce_log(dev, CE_WARN, MOD_CONFIG,
474 		    "alloc intr failed: %d %d",
475 		    navail, ret);
476 		kmem_free(dev->htable, sizeof (ddi_intr_handle_t));
477 		return (DDI_FAILURE);
478 	}
479 
480 	/* update the actual number of interrupts allocated */
481 	dev->num_vectors = navail;
482 
483 	/*
484 	 * get the interrupt priority. Assumption is that all handlers have
485 	 * equal priority
486 	 */
487 
488 	ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri);
489 
490 	if (ret != DDI_SUCCESS) {
491 		int i;
492 		oce_log(dev, CE_WARN, MOD_CONFIG,
493 		    "Unable to get intr priority: 0x%x",
494 		    dev->intr_pri);
495 		for (i = 0; i < dev->num_vectors; i++) {
496 			(void) ddi_intr_free(dev->htable[i]);
497 		}
498 		kmem_free(dev->htable, sizeof (ddi_intr_handle_t));
499 		return (DDI_FAILURE);
500 	}
501 
502 	(void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap);
503 	return (DDI_SUCCESS);
504 } /* oce_setup_intx */
505 
506 static int
507 oce_teardown_intx(struct oce_dev *dev)
508 {
509 	/* release handlers */
510 	(void) ddi_intr_free(dev->htable[0]);
511 
512 	/* release htable */
513 	kmem_free(dev->htable, sizeof (ddi_intr_handle_t));
514 
515 	return (DDI_FAILURE);
516 } /* oce_teardown_intx */
517 
518 static int
519 oce_add_intx_handlers(struct oce_dev *dev)
520 {
521 	int ret;
522 
523 	ret = ddi_intr_add_handler(dev->htable[0], oce_isr,
524 	    (caddr_t)dev->eq[0], NULL);
525 	if (ret != DDI_SUCCESS) {
526 		oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
527 		    "failed to add intr handlers");
528 		(void) ddi_intr_remove_handler(dev->htable[0]);
529 		return (DDI_FAILURE);
530 	}
531 
532 	return (DDI_SUCCESS);
533 } /* oce_add_intx_handlers */
534 
535 static void
536 oce_del_intx_handlers(struct oce_dev *dev)
537 {
538 	(void) ddi_intr_remove_handler(dev->htable[0]);
539 } /* oce_del_intx_handlers */
540