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