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