Linux-libre 4.15.7-gnu
[librecmc/linux-libre.git] / drivers / scsi / fnic / fnic_isr.c
1 /*
2  * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
3  * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
4  *
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.
8  *
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
16  * SOFTWARE.
17  */
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>
24 #include "vnic_dev.h"
25 #include "vnic_intr.h"
26 #include "vnic_stats.h"
27 #include "fnic_io.h"
28 #include "fnic.h"
29
30 static irqreturn_t fnic_isr_legacy(int irq, void *data)
31 {
32         struct fnic *fnic = data;
33         u32 pba;
34         unsigned long work_done = 0;
35
36         pba = vnic_intr_legacy_pba(fnic->legacy_pba);
37         if (!pba)
38                 return IRQ_NONE;
39
40         fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
41         atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
42
43         if (pba & (1 << FNIC_INTX_NOTIFY)) {
44                 vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]);
45                 fnic_handle_link_event(fnic);
46         }
47
48         if (pba & (1 << FNIC_INTX_ERR)) {
49                 vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_ERR]);
50                 fnic_log_q_error(fnic);
51         }
52
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);
57
58                 vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
59                                          work_done,
60                                          1 /* unmask intr */,
61                                          1 /* reset intr timer */);
62         }
63
64         return IRQ_HANDLED;
65 }
66
67 static irqreturn_t fnic_isr_msi(int irq, void *data)
68 {
69         struct fnic *fnic = data;
70         unsigned long work_done = 0;
71
72         fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
73         atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
74
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);
78
79         vnic_intr_return_credits(&fnic->intr[0],
80                                  work_done,
81                                  1 /* unmask intr */,
82                                  1 /* reset intr timer */);
83
84         return IRQ_HANDLED;
85 }
86
87 static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
88 {
89         struct fnic *fnic = data;
90         unsigned long rq_work_done = 0;
91
92         fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
93         atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
94
95         rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
96         vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
97                                  rq_work_done,
98                                  1 /* unmask intr */,
99                                  1 /* reset intr timer */);
100
101         return IRQ_HANDLED;
102 }
103
104 static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
105 {
106         struct fnic *fnic = data;
107         unsigned long wq_work_done = 0;
108
109         fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
110         atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
111
112         wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
113         vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
114                                  wq_work_done,
115                                  1 /* unmask intr */,
116                                  1 /* reset intr timer */);
117         return IRQ_HANDLED;
118 }
119
120 static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
121 {
122         struct fnic *fnic = data;
123         unsigned long wq_copy_work_done = 0;
124
125         fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
126         atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
127
128         wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
129         vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
130                                  wq_copy_work_done,
131                                  1 /* unmask intr */,
132                                  1 /* reset intr timer */);
133         return IRQ_HANDLED;
134 }
135
136 static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data)
137 {
138         struct fnic *fnic = data;
139
140         fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
141         atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
142
143         vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
144         fnic_log_q_error(fnic);
145         fnic_handle_link_event(fnic);
146
147         return IRQ_HANDLED;
148 }
149
150 void fnic_free_intr(struct fnic *fnic)
151 {
152         int i;
153
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);
158                 break;
159
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);
165                 break;
166
167         default:
168                 break;
169         }
170 }
171
172 int fnic_request_intr(struct fnic *fnic)
173 {
174         int err = 0;
175         int i;
176
177         switch (vnic_dev_get_intr_mode(fnic->vdev)) {
178
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);
182                 break;
183
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);
187                 break;
188
189         case VNIC_DEV_INTR_MODE_MSIX:
190
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;
195
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;
200
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;
205
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;
211
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);
217                         if (err) {
218                                 shost_printk(KERN_ERR, fnic->lport->host,
219                                              "MSIX: request_irq"
220                                              " failed %d\n", err);
221                                 fnic_free_intr(fnic);
222                                 break;
223                         }
224                         fnic->msix[i].requested = 1;
225                 }
226                 break;
227
228         default:
229                 break;
230         }
231
232         return err;
233 }
234
235 int fnic_set_intr_mode(struct fnic *fnic)
236 {
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);
240
241         /*
242          * Set interrupt mode (INTx, MSI, MSI-X) depending
243          * system capabilities.
244          *
245          * Try MSI-X first
246          *
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)
249          */
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;
255
256                 if (pci_alloc_irq_vectors(fnic->pdev, vecs, vecs,
257                                 PCI_IRQ_MSIX) < 0) {
258                         fnic->rq_count = n;
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;
265
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);
270                         return 0;
271                 }
272         }
273
274         /*
275          * Next try MSI
276          * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
277          */
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) {
284                 fnic->rq_count = 1;
285                 fnic->raw_wq_count = 1;
286                 fnic->wq_copy_count = 1;
287                 fnic->wq_count = 2;
288                 fnic->cq_count = 3;
289                 fnic->intr_count = 1;
290                 fnic->err_intr_offset = 0;
291
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);
295
296                 return 0;
297         }
298
299         /*
300          * Next try INTx
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
304          */
305
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) {
311
312                 fnic->rq_count = 1;
313                 fnic->raw_wq_count = 1;
314                 fnic->wq_copy_count = 1;
315                 fnic->cq_count = 3;
316                 fnic->intr_count = 3;
317
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);
321
322                 return 0;
323         }
324
325         vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
326
327         return -EINVAL;
328 }
329
330 void fnic_clear_intr_mode(struct fnic *fnic)
331 {
332         pci_free_irq_vectors(fnic->pdev);
333         vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
334 }
335