xref: /illumos-gate/usr/src/uts/common/io/fibre-channel/fca/oce/oce_intr.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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 uint_t oce_isr(caddr_t arg1, caddr_t arg2);
35 
36 /*
37  * top level function to setup interrupts
38  *
39  * dev - software handle to the device
40  *
41  * return DDI_SUCCESS => success, failure otherwise
42  */
43 int
44 oce_setup_intr(struct oce_dev *dev)
45 {
46 	int ret;
47 	int intr_types = 0;
48 	int navail = 0;
49 	int nsupported = 0;
50 	int min = 0;
51 	int nreqd = 0;
52 	int nallocd = 0;
53 
54 	/* get supported intr types */
55 	ret = ddi_intr_get_supported_types(dev->dip, &intr_types);
56 	if (ret != DDI_SUCCESS) {
57 		oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
58 		    "Failed to retrieve intr types ");
59 		return (DDI_FAILURE);
60 	}
61 
62 retry_intr:
63 	if (intr_types & DDI_INTR_TYPE_MSIX) {
64 		dev->intr_type = DDI_INTR_TYPE_MSIX;
65 		/* one vector is shared by MCC and Tx */
66 		nreqd = dev->rx_rings + 1;
67 		min = OCE_MIN_VECTORS;
68 	} else if (intr_types & DDI_INTR_TYPE_FIXED) {
69 		dev->intr_type = DDI_INTR_TYPE_FIXED;
70 		nreqd = OCE_MIN_VECTORS;
71 		min = OCE_MIN_VECTORS;
72 	}
73 
74 	ret = ddi_intr_get_nintrs(dev->dip, dev->intr_type, &nsupported);
75 	if (ret != DDI_SUCCESS) {
76 		oce_log(dev, CE_WARN, MOD_CONFIG,
77 		    "Could not get nintrs:0x%d", ret);
78 		return (DDI_FAILURE);
79 	}
80 
81 	/* get the number of vectors available */
82 	ret = ddi_intr_get_navail(dev->dip, dev->intr_type, &navail);
83 	if (ret != DDI_SUCCESS || navail < min) {
84 		oce_log(dev, CE_WARN, MOD_CONFIG,
85 		    "Could not get msix vectors:0x%x",
86 		    navail);
87 		return (DDI_FAILURE);
88 	}
89 
90 	if (navail < min) {
91 		return (DDI_FAILURE);
92 	}
93 
94 	/* if the requested number is more than available reset reqd */
95 	if (navail < nreqd) {
96 		nreqd = navail;
97 	}
98 
99 	/* allocate htable */
100 	dev->hsize  = nreqd *  sizeof (ddi_intr_handle_t);
101 	dev->htable = kmem_zalloc(dev->hsize,  KM_NOSLEEP);
102 
103 	if (dev->htable == NULL)
104 		return (DDI_FAILURE);
105 
106 	nallocd = 0;
107 	/* allocate interrupt handlers */
108 	ret = ddi_intr_alloc(dev->dip, dev->htable, dev->intr_type,
109 	    0, nreqd, &nallocd, DDI_INTR_ALLOC_NORMAL);
110 
111 	if (ret != DDI_SUCCESS) {
112 		goto fail_intr;
113 	}
114 
115 	dev->num_vectors = nallocd;
116 	if (nallocd < min) {
117 		goto fail_intr;
118 	}
119 
120 	/*
121 	 * get the interrupt priority. Assumption is that all handlers have
122 	 * equal priority
123 	 */
124 
125 	ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri);
126 
127 	if (ret != DDI_SUCCESS) {
128 		goto fail_intr;
129 	}
130 
131 	(void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap);
132 
133 	if ((intr_types & DDI_INTR_TYPE_MSIX) && (nallocd > 1)) {
134 		dev->rx_rings = nallocd - 1;
135 	} else {
136 		dev->rx_rings = 1;
137 	}
138 
139 	return (DDI_SUCCESS);
140 
141 fail_intr:
142 	(void) oce_teardown_intr(dev);
143 	if ((dev->intr_type == DDI_INTR_TYPE_MSIX) &&
144 	    (intr_types & DDI_INTR_TYPE_FIXED)) {
145 		intr_types &= ~DDI_INTR_TYPE_MSIX;
146 		oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
147 		    "Could not get MSIX vectors, trying for FIXED vectors");
148 		goto retry_intr;
149 	}
150 	return (DDI_FAILURE);
151 }
152 
153 /*
154  * top level function to undo initialization in oce_setup_intr
155  *
156  * dev - software handle to the device
157  *
158  * return DDI_SUCCESS => success, failure otherwise
159  */
160 int
161 oce_teardown_intr(struct oce_dev *dev)
162 {
163 	int i;
164 
165 	/* release handlers */
166 	for (i = 0; i < dev->num_vectors; i++) {
167 		(void) ddi_intr_free(dev->htable[i]);
168 	}
169 
170 	/* release htable */
171 	kmem_free(dev->htable, dev->hsize);
172 	dev->htable = NULL;
173 
174 	return (DDI_SUCCESS);
175 }
176 
177 /*
178  * helper function to add ISR based on interrupt type
179  *
180  * dev - software handle to the device
181  *
182  * return DDI_SUCCESS => success, failure otherwise
183  */
184 int
185 oce_setup_handlers(struct oce_dev *dev)
186 {
187 	int i = 0;
188 	int ret;
189 	for (i = 0; i < dev->num_vectors; i++) {
190 		ret = ddi_intr_add_handler(dev->htable[i], oce_isr,
191 		    (caddr_t)dev->eq[i], NULL);
192 		if (ret != DDI_SUCCESS) {
193 			oce_log(dev, CE_WARN, MOD_CONFIG, "%s",
194 			    "Failed to add interrupt handlers");
195 			for (i--; i >= 0; i--) {
196 				(void) ddi_intr_remove_handler(dev->htable[i]);
197 			}
198 			return (DDI_FAILURE);
199 		}
200 	}
201 	return (DDI_SUCCESS);
202 }
203 
204 /*
205  * helper function to remove ISRs added in oce_setup_handlers
206  *
207  * dev - software handle to the device
208  *
209  * return DDI_SUCCESS => success, failure otherwise
210  */
211 void
212 oce_remove_handler(struct oce_dev *dev)
213 {
214 	int nvec;
215 	for (nvec = 0; nvec < dev->num_vectors; nvec++) {
216 		(void) ddi_intr_remove_handler(dev->htable[nvec]);
217 	}
218 }
219 
220 void
221 oce_chip_ei(struct oce_dev *dev)
222 {
223 	uint32_t reg;
224 
225 	reg =  OCE_CFG_READ32(dev, PCICFG_INTR_CTRL);
226 	if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) {
227 		ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
228 	}
229 	reg |= HOSTINTR_MASK;
230 	OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg);
231 	if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) {
232 		ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
233 	}
234 }
235 
236 /*
237  * function to enable interrupts
238  *
239  * dev - software handle to the device
240  *
241  * return DDI_SUCCESS => success, failure otherwise
242  */
243 void
244 oce_ei(struct oce_dev *dev)
245 {
246 	int i;
247 	int ret;
248 
249 	if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
250 		(void) ddi_intr_block_enable(dev->htable, dev->num_vectors);
251 	} else {
252 
253 		for (i = 0; i < dev->num_vectors; i++) {
254 			ret = ddi_intr_enable(dev->htable[i]);
255 			if (ret != DDI_SUCCESS) {
256 				for (i--; i >= 0; i--) {
257 					(void) ddi_intr_disable(dev->htable[i]);
258 				}
259 			}
260 		}
261 	}
262 	oce_chip_ei(dev);
263 } /* oce_ei */
264 
265 void
266 oce_chip_di(struct oce_dev *dev)
267 {
268 	uint32_t reg;
269 
270 	reg =  OCE_CFG_READ32(dev, PCICFG_INTR_CTRL);
271 	if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) {
272 		ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
273 	}
274 	reg &= ~HOSTINTR_MASK;
275 	OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg);
276 	if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) {
277 		ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED);
278 	}
279 }
280 
281 /*
282  * function to disable interrupts
283  *
284  * dev - software handle to the device
285  *
286  * return DDI_SUCCESS => success, failure otherwise
287  */
288 void
289 oce_di(struct oce_dev *dev)
290 {
291 	int i;
292 	int ret;
293 
294 	oce_chip_di(dev);
295 	if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) {
296 		(void) ddi_intr_block_disable(dev->htable, dev->num_vectors);
297 	} else {
298 		for (i = 0; i < dev->num_vectors; i++) {
299 			ret = ddi_intr_disable(dev->htable[i]);
300 			if (ret != DDI_SUCCESS) {
301 				oce_log(dev, CE_WARN, MOD_CONFIG,
302 				    "Failed to disable interrupts 0x%x", ret);
303 			}
304 		}
305 	}
306 } /* oce_di */
307 
308 /*
309  * command interrupt handler routine added to all vectors
310  *
311  * arg1 = callback data
312  * arg2 - callback data
313  *
314  * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR
315  */
316 static uint_t
317 oce_isr(caddr_t arg1, caddr_t arg2)
318 {
319 	struct oce_eq *eq;
320 	struct oce_eqe *eqe;
321 	uint16_t num_eqe = 0;
322 	uint16_t cq_id;
323 	struct oce_cq *cq;
324 	struct oce_dev  *dev;
325 
326 	_NOTE(ARGUNUSED(arg2));
327 
328 	eq = (struct oce_eq *)(void *)(arg1);
329 
330 	dev = eq->parent;
331 
332 	eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
333 
334 	while (eqe->u0.dw0) {
335 
336 		eqe->u0.dw0 = LE_32(eqe->u0.dw0);
337 
338 		/* if not CQ then continue else flag an error */
339 		if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) {
340 			oce_log(dev, CE_WARN, MOD_ISR,
341 			    "NOT a CQ event. 0x%x",
342 			    eqe->u0.s.major_code);
343 		}
344 
345 		/* get the cq from the eqe */
346 		cq_id = eqe->u0.s.resource_id % OCE_MAX_CQ;
347 		cq = dev->cq[cq_id];
348 
349 		/* Call the completion handler */
350 		(void) cq->cq_handler(cq->cb_arg);
351 
352 		/* clear valid bit and progress eqe */
353 		eqe->u0.dw0 = 0;
354 		RING_GET(eq->ring, 1);
355 		eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe);
356 		num_eqe++;
357 	} /* for all EQEs */
358 
359 	/* ring the eq doorbell, signify that it's done processing  */
360 	oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE);
361 	if (num_eqe > 0) {
362 		return (DDI_INTR_CLAIMED);
363 	} else {
364 		return (DDI_INTR_UNCLAIMED);
365 	}
366 } /* oce_msix_handler */
367