- patches.suse/slab-handle-memoryless-nodes-v2a.patch: Refresh.
[linux-flexiantxendom0-3.2.10.git] / drivers / scsi / bfa / bfad_intr.c
1 /*
2  * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
3  * All rights reserved
4  * www.brocade.com
5  *
6  * Linux driver for Brocade Fibre Channel Host Bus Adapter.
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License (GPL) Version 2 as
10  * published by the Free Software Foundation
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  */
17
18 #include "bfad_drv.h"
19 #include "bfad_trcmod.h"
20
21 BFA_TRC_FILE(LDRV, INTR);
22
23 /**
24  *  bfa_isr BFA driver interrupt functions
25  */
26 static int msix_disable;
27 module_param(msix_disable, int, S_IRUGO | S_IWUSR);
28 /**
29  * Line based interrupt handler.
30  */
31 static irqreturn_t
32 bfad_intx(int irq, void *dev_id)
33 {
34         struct bfad_s         *bfad = dev_id;
35         struct list_head         doneq;
36         unsigned long   flags;
37         bfa_boolean_t rc;
38
39         spin_lock_irqsave(&bfad->bfad_lock, flags);
40         rc = bfa_intx(&bfad->bfa);
41         if (!rc) {
42                 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
43                 return IRQ_NONE;
44         }
45
46         bfa_comp_deq(&bfad->bfa, &doneq);
47         spin_unlock_irqrestore(&bfad->bfad_lock, flags);
48
49         if (!list_empty(&doneq)) {
50                 bfa_comp_process(&bfad->bfa, &doneq);
51
52                 spin_lock_irqsave(&bfad->bfad_lock, flags);
53                 bfa_comp_free(&bfad->bfa, &doneq);
54                 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
55                 bfa_trc_fp(bfad, irq);
56         }
57
58         return IRQ_HANDLED;
59
60 }
61
62 static irqreturn_t
63 bfad_msix(int irq, void *dev_id)
64 {
65         struct bfad_msix_s *vec = dev_id;
66         struct bfad_s *bfad = vec->bfad;
67         struct list_head doneq;
68         unsigned long   flags;
69
70         spin_lock_irqsave(&bfad->bfad_lock, flags);
71
72         bfa_msix(&bfad->bfa, vec->msix.entry);
73         bfa_comp_deq(&bfad->bfa, &doneq);
74         spin_unlock_irqrestore(&bfad->bfad_lock, flags);
75
76         if (!list_empty(&doneq)) {
77                 bfa_comp_process(&bfad->bfa, &doneq);
78
79                 spin_lock_irqsave(&bfad->bfad_lock, flags);
80                 bfa_comp_free(&bfad->bfa, &doneq);
81                 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
82         }
83
84         return IRQ_HANDLED;
85 }
86
87 /**
88  * Initialize the MSIX entry table.
89  */
90 static void
91 bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries,
92                          int mask, int max_bit)
93 {
94         int             i;
95         int             match = 0x00000001;
96
97         for (i = 0, bfad->nvec = 0; i < MAX_MSIX_ENTRY; i++) {
98                 if (mask & match) {
99                         bfad->msix_tab[bfad->nvec].msix.entry = i;
100                         bfad->msix_tab[bfad->nvec].bfad = bfad;
101                         msix_entries[bfad->nvec].entry = i;
102                         bfad->nvec++;
103                 }
104
105                 match <<= 1;
106         }
107
108 }
109
110 int
111 bfad_install_msix_handler(struct bfad_s *bfad)
112 {
113         int             i, error = 0;
114
115         for (i = 0; i < bfad->nvec; i++) {
116                 error = request_irq(bfad->msix_tab[i].msix.vector,
117                                     (irq_handler_t) bfad_msix, 0,
118                                     BFAD_DRIVER_NAME, &bfad->msix_tab[i]);
119                 bfa_trc(bfad, i);
120                 bfa_trc(bfad, bfad->msix_tab[i].msix.vector);
121                 if (error) {
122                         int             j;
123
124                         for (j = 0; j < i; j++)
125                                 free_irq(bfad->msix_tab[j].msix.vector,
126                                                 &bfad->msix_tab[j]);
127
128                         return 1;
129                 }
130         }
131
132         return 0;
133 }
134
135 /**
136  * Setup MSIX based interrupt.
137  */
138 int
139 bfad_setup_intr(struct bfad_s *bfad)
140 {
141         int error = 0;
142         u32 mask = 0, i, num_bit = 0, max_bit = 0;
143         struct msix_entry msix_entries[MAX_MSIX_ENTRY];
144
145         /* Call BFA to get the msix map for this PCI function.  */
146         bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit);
147
148         /* Set up the msix entry table */
149         bfad_init_msix_entry(bfad, msix_entries, mask, max_bit);
150
151         if (!msix_disable) {
152                 error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec);
153                 if (error) {
154                         /*
155                          * Only error number of vector is available.
156                          * We don't have a mechanism to map multiple
157                          * interrupts into one vector, so even if we
158                          * can try to request less vectors, we don't
159                          * know how to associate interrupt events to
160                          *  vectors. Linux doesn't dupicate vectors
161                          * in the MSIX table for this case.
162                          */
163
164                         printk(KERN_WARNING "bfad%d: "
165                                 "pci_enable_msix failed (%d),"
166                                 " use line based.\n", bfad->inst_no, error);
167
168                         goto line_based;
169                 }
170
171                 /* Save the vectors */
172                 for (i = 0; i < bfad->nvec; i++) {
173                         bfa_trc(bfad, msix_entries[i].vector);
174                         bfad->msix_tab[i].msix.vector = msix_entries[i].vector;
175                 }
176
177                 bfa_msix_init(&bfad->bfa, bfad->nvec);
178
179                 bfad->bfad_flags |= BFAD_MSIX_ON;
180
181                 return error;
182         }
183
184 line_based:
185         error = 0;
186         if (request_irq
187             (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS,
188              BFAD_DRIVER_NAME, bfad) != 0) {
189                 /* Enable interrupt handler failed */
190                 return 1;
191         }
192
193         return error;
194 }
195
196 void
197 bfad_remove_intr(struct bfad_s *bfad)
198 {
199         int             i;
200
201         if (bfad->bfad_flags & BFAD_MSIX_ON) {
202                 for (i = 0; i < bfad->nvec; i++)
203                         free_irq(bfad->msix_tab[i].msix.vector,
204                                         &bfad->msix_tab[i]);
205
206                 pci_disable_msix(bfad->pcidev);
207                 bfad->bfad_flags &= ~BFAD_MSIX_ON;
208         } else {
209                 free_irq(bfad->pcidev->irq, bfad);
210         }
211 }
212
213