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
, io_completions
);
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
, io_completions
);
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
, io_completions
);
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(pci_irq_vector(fnic
->pdev
, 0), 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(pci_irq_vector(fnic
->pdev
, i
),
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(pci_irq_vector(fnic
->pdev
, 0),
181 &fnic_isr_legacy
, IRQF_SHARED
, DRV_NAME
, fnic
);
184 case VNIC_DEV_INTR_MODE_MSI
:
185 err
= request_irq(pci_irq_vector(fnic
->pdev
, 0), &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(pci_irq_vector(fnic
->pdev
, i
),
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
);
242 * Set interrupt mode (INTx, MSI, MSI-X) depending
243 * system capabilities.
247 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
248 * (last INTR is used for WQ/RQ errors and notification area)
250 if (fnic
->rq_count
>= n
&&
251 fnic
->raw_wq_count
>= m
&&
252 fnic
->wq_copy_count
>= o
&&
253 fnic
->cq_count
>= n
+ m
+ o
) {
254 int vecs
= n
+ m
+ o
+ 1;
256 if (pci_alloc_irq_vectors(fnic
->pdev
, vecs
, vecs
,
259 fnic
->raw_wq_count
= m
;
260 fnic
->wq_copy_count
= o
;
261 fnic
->wq_count
= m
+ o
;
262 fnic
->cq_count
= n
+ m
+ o
;
263 fnic
->intr_count
= vecs
;
264 fnic
->err_intr_offset
= FNIC_MSIX_ERR_NOTIFY
;
266 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
267 "Using MSI-X Interrupts\n");
268 vnic_dev_set_intr_mode(fnic
->vdev
,
269 VNIC_DEV_INTR_MODE_MSIX
);
276 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
278 if (fnic
->rq_count
>= 1 &&
279 fnic
->raw_wq_count
>= 1 &&
280 fnic
->wq_copy_count
>= 1 &&
281 fnic
->cq_count
>= 3 &&
282 fnic
->intr_count
>= 1 &&
283 pci_alloc_irq_vectors(fnic
->pdev
, 1, 1, PCI_IRQ_MSI
) < 0) {
285 fnic
->raw_wq_count
= 1;
286 fnic
->wq_copy_count
= 1;
289 fnic
->intr_count
= 1;
290 fnic
->err_intr_offset
= 0;
292 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
293 "Using MSI Interrupts\n");
294 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_MSI
);
301 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
302 * 1 INTR is used for all 3 queues, 1 INTR for queue errors
303 * 1 INTR for notification area
306 if (fnic
->rq_count
>= 1 &&
307 fnic
->raw_wq_count
>= 1 &&
308 fnic
->wq_copy_count
>= 1 &&
309 fnic
->cq_count
>= 3 &&
310 fnic
->intr_count
>= 3) {
313 fnic
->raw_wq_count
= 1;
314 fnic
->wq_copy_count
= 1;
316 fnic
->intr_count
= 3;
318 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
,
319 "Using Legacy Interrupts\n");
320 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);
325 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_UNKNOWN
);
330 void fnic_clear_intr_mode(struct fnic
*fnic
)
332 pci_free_irq_vectors(fnic
->pdev
);
333 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);