2 * Copyright 2008 Cisco Systems, Inc. All rights reserved.
3 * Copyright 2007 Nuova Systems, Inc. All rights reserved.
5 * This program is free software; you may redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
9 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
10 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
11 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
12 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
13 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
14 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 #include <linux/string.h>
19 #include <linux/errno.h>
20 #include <linux/pci.h>
21 #include <linux/interrupt.h>
22 #include <scsi/libfc.h>
23 #include <scsi/fc_frame.h>
25 #include "vnic_intr.h"
26 #include "vnic_stats.h"
30 static irqreturn_t
fnic_isr_legacy(int irq
, void *data
)
32 struct fnic
*fnic
= data
;
34 unsigned long work_done
= 0;
36 pba
= vnic_intr_legacy_pba(fnic
->legacy_pba
);
40 if (pba
& (1 << FNIC_INTX_NOTIFY
)) {
41 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_NOTIFY
]);
42 fnic_handle_link_event(fnic
);
45 if (pba
& (1 << FNIC_INTX_ERR
)) {
46 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_ERR
]);
47 fnic_log_q_error(fnic
);
50 if (pba
& (1 << FNIC_INTX_WQ_RQ_COPYWQ
)) {
51 work_done
+= fnic_wq_copy_cmpl_handler(fnic
, -1);
52 work_done
+= fnic_wq_cmpl_handler(fnic
, -1);
53 work_done
+= fnic_rq_cmpl_handler(fnic
, -1);
55 vnic_intr_return_credits(&fnic
->intr
[FNIC_INTX_WQ_RQ_COPYWQ
],
58 1 /* reset intr timer */);
64 static irqreturn_t
fnic_isr_msi(int irq
, void *data
)
66 struct fnic
*fnic
= data
;
67 unsigned long work_done
= 0;
69 work_done
+= fnic_wq_copy_cmpl_handler(fnic
, -1);
70 work_done
+= fnic_wq_cmpl_handler(fnic
, -1);
71 work_done
+= fnic_rq_cmpl_handler(fnic
, -1);
73 vnic_intr_return_credits(&fnic
->intr
[0],
76 1 /* reset intr timer */);
81 static irqreturn_t
fnic_isr_msix_rq(int irq
, void *data
)
83 struct fnic
*fnic
= data
;
84 unsigned long rq_work_done
= 0;
86 rq_work_done
= fnic_rq_cmpl_handler(fnic
, -1);
87 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_RQ
],
90 1 /* reset intr timer */);
95 static irqreturn_t
fnic_isr_msix_wq(int irq
, void *data
)
97 struct fnic
*fnic
= data
;
98 unsigned long wq_work_done
= 0;
100 wq_work_done
= fnic_wq_cmpl_handler(fnic
, -1);
101 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_WQ
],
104 1 /* reset intr timer */);
108 static irqreturn_t
fnic_isr_msix_wq_copy(int irq
, void *data
)
110 struct fnic
*fnic
= data
;
111 unsigned long wq_copy_work_done
= 0;
113 wq_copy_work_done
= fnic_wq_copy_cmpl_handler(fnic
, -1);
114 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_WQ_COPY
],
117 1 /* reset intr timer */);
121 static irqreturn_t
fnic_isr_msix_err_notify(int irq
, void *data
)
123 struct fnic
*fnic
= data
;
125 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_MSIX_ERR_NOTIFY
]);
126 fnic_log_q_error(fnic
);
127 fnic_handle_link_event(fnic
);
132 void fnic_free_intr(struct fnic
*fnic
)
136 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
137 case VNIC_DEV_INTR_MODE_INTX
:
138 case VNIC_DEV_INTR_MODE_MSI
:
139 free_irq(fnic
->pdev
->irq
, fnic
);
142 case VNIC_DEV_INTR_MODE_MSIX
:
143 for (i
= 0; i
< ARRAY_SIZE(fnic
->msix
); i
++)
144 if (fnic
->msix
[i
].requested
)
145 free_irq(fnic
->msix_entry
[i
].vector
,
146 fnic
->msix
[i
].devid
);
154 int fnic_request_intr(struct fnic
*fnic
)
159 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
161 case VNIC_DEV_INTR_MODE_INTX
:
162 err
= request_irq(fnic
->pdev
->irq
, &fnic_isr_legacy
,
163 IRQF_SHARED
, DRV_NAME
, fnic
);
166 case VNIC_DEV_INTR_MODE_MSI
:
167 err
= request_irq(fnic
->pdev
->irq
, &fnic_isr_msi
,
168 0, fnic
->name
, fnic
);
171 case VNIC_DEV_INTR_MODE_MSIX
:
173 sprintf(fnic
->msix
[FNIC_MSIX_RQ
].devname
,
174 "%.11s-fcs-rq", fnic
->name
);
175 fnic
->msix
[FNIC_MSIX_RQ
].isr
= fnic_isr_msix_rq
;
176 fnic
->msix
[FNIC_MSIX_RQ
].devid
= fnic
;
178 sprintf(fnic
->msix
[FNIC_MSIX_WQ
].devname
,
179 "%.11s-fcs-wq", fnic
->name
);
180 fnic
->msix
[FNIC_MSIX_WQ
].isr
= fnic_isr_msix_wq
;
181 fnic
->msix
[FNIC_MSIX_WQ
].devid
= fnic
;
183 sprintf(fnic
->msix
[FNIC_MSIX_WQ_COPY
].devname
,
184 "%.11s-scsi-wq", fnic
->name
);
185 fnic
->msix
[FNIC_MSIX_WQ_COPY
].isr
= fnic_isr_msix_wq_copy
;
186 fnic
->msix
[FNIC_MSIX_WQ_COPY
].devid
= fnic
;
188 sprintf(fnic
->msix
[FNIC_MSIX_ERR_NOTIFY
].devname
,
189 "%.11s-err-notify", fnic
->name
);
190 fnic
->msix
[FNIC_MSIX_ERR_NOTIFY
].isr
=
191 fnic_isr_msix_err_notify
;
192 fnic
->msix
[FNIC_MSIX_ERR_NOTIFY
].devid
= fnic
;
194 for (i
= 0; i
< ARRAY_SIZE(fnic
->msix
); i
++) {
195 err
= request_irq(fnic
->msix_entry
[i
].vector
,
196 fnic
->msix
[i
].isr
, 0,
197 fnic
->msix
[i
].devname
,
198 fnic
->msix
[i
].devid
);
200 shost_printk(KERN_ERR
, fnic
->lport
->host
,
202 " failed %d\n", err
);
203 fnic_free_intr(fnic
);
206 fnic
->msix
[i
].requested
= 1;
217 int fnic_set_intr_mode(struct fnic
*fnic
)
219 unsigned int n
= ARRAY_SIZE(fnic
->rq
);
220 unsigned int m
= ARRAY_SIZE(fnic
->wq
);
221 unsigned int o
= ARRAY_SIZE(fnic
->wq_copy
);
225 * Set interrupt mode (INTx, MSI, MSI-X) depending
226 * system capabilities.
230 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
231 * (last INTR is used for WQ/RQ errors and notification area)
234 BUG_ON(ARRAY_SIZE(fnic
->msix_entry
) < n
+ m
+ o
+ 1);
235 for (i
= 0; i
< n
+ m
+ o
+ 1; i
++)
236 fnic
->msix_entry
[i
].entry
= i
;
238 if (fnic
->rq_count
>= n
&&
239 fnic
->raw_wq_count
>= m
&&
240 fnic
->wq_copy_count
>= o
&&
241 fnic
->cq_count
>= n
+ m
+ o
) {
242 if (!pci_enable_msix(fnic
->pdev
, fnic
->msix_entry
,
245 fnic
->raw_wq_count
= m
;
246 fnic
->wq_copy_count
= o
;
247 fnic
->wq_count
= m
+ o
;
248 fnic
->cq_count
= n
+ m
+ o
;
249 fnic
->intr_count
= n
+ m
+ o
+ 1;
250 fnic
->err_intr_offset
= FNIC_MSIX_ERR_NOTIFY
;
252 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
253 "Using MSI-X Interrupts\n");
254 vnic_dev_set_intr_mode(fnic
->vdev
,
255 VNIC_DEV_INTR_MODE_MSIX
);
262 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
264 if (fnic
->rq_count
>= 1 &&
265 fnic
->raw_wq_count
>= 1 &&
266 fnic
->wq_copy_count
>= 1 &&
267 fnic
->cq_count
>= 3 &&
268 fnic
->intr_count
>= 1 &&
269 !pci_enable_msi(fnic
->pdev
)) {
272 fnic
->raw_wq_count
= 1;
273 fnic
->wq_copy_count
= 1;
276 fnic
->intr_count
= 1;
277 fnic
->err_intr_offset
= 0;
279 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
280 "Using MSI Interrupts\n");
281 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_MSI
);
288 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
289 * 1 INTR is used for all 3 queues, 1 INTR for queue errors
290 * 1 INTR for notification area
293 if (fnic
->rq_count
>= 1 &&
294 fnic
->raw_wq_count
>= 1 &&
295 fnic
->wq_copy_count
>= 1 &&
296 fnic
->cq_count
>= 3 &&
297 fnic
->intr_count
>= 3) {
300 fnic
->raw_wq_count
= 1;
301 fnic
->wq_copy_count
= 1;
303 fnic
->intr_count
= 3;
305 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
306 "Using Legacy Interrupts\n");
307 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);
312 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_UNKNOWN
);
317 void fnic_clear_intr_mode(struct fnic
*fnic
)
319 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
320 case VNIC_DEV_INTR_MODE_MSIX
:
321 pci_disable_msix(fnic
->pdev
);
323 case VNIC_DEV_INTR_MODE_MSI
:
324 pci_disable_msi(fnic
->pdev
);
330 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);