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