Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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(fnic->pdev->irq, 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(fnic->msix_entry[i].vector,
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(fnic->pdev->irq, &fnic_isr_legacy,
181                                   IRQF_SHARED, DRV_NAME, fnic);
182                 break;
183
184         case VNIC_DEV_INTR_MODE_MSI:
185                 err = request_irq(fnic->pdev->irq, &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(fnic->msix_entry[i].vector,
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         unsigned int i;
241
242         /*
243          * Set interrupt mode (INTx, MSI, MSI-X) depending
244          * system capabilities.
245          *
246          * Try MSI-X first
247          *
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)
250          */
251
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;
255
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_exact(fnic->pdev, fnic->msix_entry,
261                                            n + m + o + 1)) {
262                         fnic->rq_count = n;
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;
269
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);
274                         return 0;
275                 }
276         }
277
278         /*
279          * Next try MSI
280          * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
281          */
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)) {
288
289                 fnic->rq_count = 1;
290                 fnic->raw_wq_count = 1;
291                 fnic->wq_copy_count = 1;
292                 fnic->wq_count = 2;
293                 fnic->cq_count = 3;
294                 fnic->intr_count = 1;
295                 fnic->err_intr_offset = 0;
296
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);
300
301                 return 0;
302         }
303
304         /*
305          * Next try INTx
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
309          */
310
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) {
316
317                 fnic->rq_count = 1;
318                 fnic->raw_wq_count = 1;
319                 fnic->wq_copy_count = 1;
320                 fnic->cq_count = 3;
321                 fnic->intr_count = 3;
322
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);
326
327                 return 0;
328         }
329
330         vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
331
332         return -EINVAL;
333 }
334
335 void fnic_clear_intr_mode(struct fnic *fnic)
336 {
337         switch (vnic_dev_get_intr_mode(fnic->vdev)) {
338         case VNIC_DEV_INTR_MODE_MSIX:
339                 pci_disable_msix(fnic->pdev);
340                 break;
341         case VNIC_DEV_INTR_MODE_MSI:
342                 pci_disable_msi(fnic->pdev);
343                 break;
344         default:
345                 break;
346         }
347
348         vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
349 }
350