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 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
41 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
43 if (pba
& (1 << FNIC_INTX_NOTIFY
)) {
44 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_NOTIFY
]);
45 fnic_handle_link_event(fnic
);
48 if (pba
& (1 << FNIC_INTX_ERR
)) {
49 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_ERR
]);
50 fnic_log_q_error(fnic
);
53 if (pba
& (1 << FNIC_INTX_WQ_RQ_COPYWQ
)) {
54 work_done
+= fnic_wq_copy_cmpl_handler(fnic
, -1);
55 work_done
+= fnic_wq_cmpl_handler(fnic
, -1);
56 work_done
+= fnic_rq_cmpl_handler(fnic
, -1);
58 vnic_intr_return_credits(&fnic
->intr
[FNIC_INTX_WQ_RQ_COPYWQ
],
61 1 /* reset intr timer */);
67 static irqreturn_t
fnic_isr_msi(int irq
, void *data
)
69 struct fnic
*fnic
= data
;
70 unsigned long work_done
= 0;
72 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
73 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
75 work_done
+= fnic_wq_copy_cmpl_handler(fnic
, -1);
76 work_done
+= fnic_wq_cmpl_handler(fnic
, -1);
77 work_done
+= fnic_rq_cmpl_handler(fnic
, -1);
79 vnic_intr_return_credits(&fnic
->intr
[0],
82 1 /* reset intr timer */);
87 static irqreturn_t
fnic_isr_msix_rq(int irq
, void *data
)
89 struct fnic
*fnic
= data
;
90 unsigned long rq_work_done
= 0;
92 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
93 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
95 rq_work_done
= fnic_rq_cmpl_handler(fnic
, -1);
96 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_RQ
],
99 1 /* reset intr timer */);
104 static irqreturn_t
fnic_isr_msix_wq(int irq
, void *data
)
106 struct fnic
*fnic
= data
;
107 unsigned long wq_work_done
= 0;
109 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
110 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
112 wq_work_done
= fnic_wq_cmpl_handler(fnic
, -1);
113 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_WQ
],
116 1 /* reset intr timer */);
120 static irqreturn_t
fnic_isr_msix_wq_copy(int irq
, void *data
)
122 struct fnic
*fnic
= data
;
123 unsigned long wq_copy_work_done
= 0;
125 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
126 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
128 wq_copy_work_done
= fnic_wq_copy_cmpl_handler(fnic
, -1);
129 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_WQ_COPY
],
132 1 /* reset intr timer */);
136 static irqreturn_t
fnic_isr_msix_err_notify(int irq
, void *data
)
138 struct fnic
*fnic
= data
;
140 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
141 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
143 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_MSIX_ERR_NOTIFY
]);
144 fnic_log_q_error(fnic
);
145 fnic_handle_link_event(fnic
);
150 void fnic_free_intr(struct fnic
*fnic
)
154 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
155 case VNIC_DEV_INTR_MODE_INTX
:
156 case VNIC_DEV_INTR_MODE_MSI
:
157 free_irq(fnic
->pdev
->irq
, fnic
);
160 case VNIC_DEV_INTR_MODE_MSIX
:
161 for (i
= 0; i
< ARRAY_SIZE(fnic
->msix
); i
++)
162 if (fnic
->msix
[i
].requested
)
163 free_irq(fnic
->msix_entry
[i
].vector
,
164 fnic
->msix
[i
].devid
);
172 int fnic_request_intr(struct fnic
*fnic
)
177 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
179 case VNIC_DEV_INTR_MODE_INTX
:
180 err
= request_irq(fnic
->pdev
->irq
, &fnic_isr_legacy
,
181 IRQF_SHARED
, DRV_NAME
, fnic
);
184 case VNIC_DEV_INTR_MODE_MSI
:
185 err
= request_irq(fnic
->pdev
->irq
, &fnic_isr_msi
,
186 0, fnic
->name
, fnic
);
189 case VNIC_DEV_INTR_MODE_MSIX
:
191 sprintf(fnic
->msix
[FNIC_MSIX_RQ
].devname
,
192 "%.11s-fcs-rq", fnic
->name
);
193 fnic
->msix
[FNIC_MSIX_RQ
].isr
= fnic_isr_msix_rq
;
194 fnic
->msix
[FNIC_MSIX_RQ
].devid
= fnic
;
196 sprintf(fnic
->msix
[FNIC_MSIX_WQ
].devname
,
197 "%.11s-fcs-wq", fnic
->name
);
198 fnic
->msix
[FNIC_MSIX_WQ
].isr
= fnic_isr_msix_wq
;
199 fnic
->msix
[FNIC_MSIX_WQ
].devid
= fnic
;
201 sprintf(fnic
->msix
[FNIC_MSIX_WQ_COPY
].devname
,
202 "%.11s-scsi-wq", fnic
->name
);
203 fnic
->msix
[FNIC_MSIX_WQ_COPY
].isr
= fnic_isr_msix_wq_copy
;
204 fnic
->msix
[FNIC_MSIX_WQ_COPY
].devid
= fnic
;
206 sprintf(fnic
->msix
[FNIC_MSIX_ERR_NOTIFY
].devname
,
207 "%.11s-err-notify", fnic
->name
);
208 fnic
->msix
[FNIC_MSIX_ERR_NOTIFY
].isr
=
209 fnic_isr_msix_err_notify
;
210 fnic
->msix
[FNIC_MSIX_ERR_NOTIFY
].devid
= fnic
;
212 for (i
= 0; i
< ARRAY_SIZE(fnic
->msix
); i
++) {
213 err
= request_irq(fnic
->msix_entry
[i
].vector
,
214 fnic
->msix
[i
].isr
, 0,
215 fnic
->msix
[i
].devname
,
216 fnic
->msix
[i
].devid
);
218 shost_printk(KERN_ERR
, fnic
->lport
->host
,
220 " failed %d\n", err
);
221 fnic_free_intr(fnic
);
224 fnic
->msix
[i
].requested
= 1;
235 int fnic_set_intr_mode(struct fnic
*fnic
)
237 unsigned int n
= ARRAY_SIZE(fnic
->rq
);
238 unsigned int m
= ARRAY_SIZE(fnic
->wq
);
239 unsigned int o
= ARRAY_SIZE(fnic
->wq_copy
);
243 * Set interrupt mode (INTx, MSI, MSI-X) depending
244 * system capabilities.
248 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
249 * (last INTR is used for WQ/RQ errors and notification area)
252 BUG_ON(ARRAY_SIZE(fnic
->msix_entry
) < n
+ m
+ o
+ 1);
253 for (i
= 0; i
< n
+ m
+ o
+ 1; i
++)
254 fnic
->msix_entry
[i
].entry
= i
;
256 if (fnic
->rq_count
>= n
&&
257 fnic
->raw_wq_count
>= m
&&
258 fnic
->wq_copy_count
>= o
&&
259 fnic
->cq_count
>= n
+ m
+ o
) {
260 if (!pci_enable_msix(fnic
->pdev
, fnic
->msix_entry
,
263 fnic
->raw_wq_count
= m
;
264 fnic
->wq_copy_count
= o
;
265 fnic
->wq_count
= m
+ o
;
266 fnic
->cq_count
= n
+ m
+ o
;
267 fnic
->intr_count
= n
+ m
+ o
+ 1;
268 fnic
->err_intr_offset
= FNIC_MSIX_ERR_NOTIFY
;
270 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
271 "Using MSI-X Interrupts\n");
272 vnic_dev_set_intr_mode(fnic
->vdev
,
273 VNIC_DEV_INTR_MODE_MSIX
);
280 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
282 if (fnic
->rq_count
>= 1 &&
283 fnic
->raw_wq_count
>= 1 &&
284 fnic
->wq_copy_count
>= 1 &&
285 fnic
->cq_count
>= 3 &&
286 fnic
->intr_count
>= 1 &&
287 !pci_enable_msi(fnic
->pdev
)) {
290 fnic
->raw_wq_count
= 1;
291 fnic
->wq_copy_count
= 1;
294 fnic
->intr_count
= 1;
295 fnic
->err_intr_offset
= 0;
297 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
298 "Using MSI Interrupts\n");
299 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_MSI
);
306 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
307 * 1 INTR is used for all 3 queues, 1 INTR for queue errors
308 * 1 INTR for notification area
311 if (fnic
->rq_count
>= 1 &&
312 fnic
->raw_wq_count
>= 1 &&
313 fnic
->wq_copy_count
>= 1 &&
314 fnic
->cq_count
>= 3 &&
315 fnic
->intr_count
>= 3) {
318 fnic
->raw_wq_count
= 1;
319 fnic
->wq_copy_count
= 1;
321 fnic
->intr_count
= 3;
323 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
324 "Using Legacy Interrupts\n");
325 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);
330 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_UNKNOWN
);
335 void fnic_clear_intr_mode(struct fnic
*fnic
)
337 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
338 case VNIC_DEV_INTR_MODE_MSIX
:
339 pci_disable_msix(fnic
->pdev
);
341 case VNIC_DEV_INTR_MODE_MSI
:
342 pci_disable_msi(fnic
->pdev
);
348 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);