]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/scsi/bfa/bfad_intr.c
Merge branch 'twl4030-mfd' into for-2.6.33
[mv-sheeva.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 irqreturn_t bfad_intx(int irq, void *dev_id);
27 static int msix_disable;
28 module_param(msix_disable, int, S_IRUGO | S_IWUSR);
29 /**
30  * Line based interrupt handler.
31  */
32 irqreturn_t
33 bfad_intx(int irq, void *dev_id)
34 {
35         struct bfad_s         *bfad = dev_id;
36         struct list_head         doneq;
37         unsigned long   flags;
38         bfa_boolean_t rc;
39
40         spin_lock_irqsave(&bfad->bfad_lock, flags);
41         rc = bfa_intx(&bfad->bfa);
42         if (!rc) {
43                 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
44                 return IRQ_NONE;
45         }
46
47         bfa_comp_deq(&bfad->bfa, &doneq);
48         spin_unlock_irqrestore(&bfad->bfad_lock, flags);
49
50         if (!list_empty(&doneq)) {
51                 bfa_comp_process(&bfad->bfa, &doneq);
52
53                 spin_lock_irqsave(&bfad->bfad_lock, flags);
54                 bfa_comp_free(&bfad->bfa, &doneq);
55                 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
56                 bfa_trc_fp(bfad, irq);
57         }
58
59         return IRQ_HANDLED;
60
61 }
62
63 static irqreturn_t
64 bfad_msix(int irq, void *dev_id)
65 {
66         struct bfad_msix_s *vec = dev_id;
67         struct bfad_s *bfad = vec->bfad;
68         struct list_head doneq;
69         unsigned long   flags;
70
71         spin_lock_irqsave(&bfad->bfad_lock, flags);
72
73         bfa_msix(&bfad->bfa, vec->msix.entry);
74         bfa_comp_deq(&bfad->bfa, &doneq);
75         spin_unlock_irqrestore(&bfad->bfad_lock, flags);
76
77         if (!list_empty(&doneq)) {
78                 bfa_comp_process(&bfad->bfa, &doneq);
79
80                 spin_lock_irqsave(&bfad->bfad_lock, flags);
81                 bfa_comp_free(&bfad->bfa, &doneq);
82                 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
83         }
84
85         return IRQ_HANDLED;
86 }
87
88 /**
89  * Initialize the MSIX entry table.
90  */
91 static void
92 bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries,
93                          int mask, int max_bit)
94 {
95         int             i;
96         int             match = 0x00000001;
97
98         for (i = 0, bfad->nvec = 0; i < MAX_MSIX_ENTRY; i++) {
99                 if (mask & match) {
100                         bfad->msix_tab[bfad->nvec].msix.entry = i;
101                         bfad->msix_tab[bfad->nvec].bfad = bfad;
102                         msix_entries[bfad->nvec].entry = i;
103                         bfad->nvec++;
104                 }
105
106                 match <<= 1;
107         }
108
109 }
110
111 int
112 bfad_install_msix_handler(struct bfad_s *bfad)
113 {
114         int             i, error = 0;
115
116         for (i = 0; i < bfad->nvec; i++) {
117                 error = request_irq(bfad->msix_tab[i].msix.vector,
118                                     (irq_handler_t) bfad_msix, 0,
119                                     BFAD_DRIVER_NAME, &bfad->msix_tab[i]);
120                 bfa_trc(bfad, i);
121                 bfa_trc(bfad, bfad->msix_tab[i].msix.vector);
122                 if (error) {
123                         int             j;
124
125                         for (j = 0; j < i; j++)
126                                 free_irq(bfad->msix_tab[j].msix.vector,
127                                                 &bfad->msix_tab[j]);
128
129                         return 1;
130                 }
131         }
132
133         return 0;
134 }
135
136 /**
137  * Setup MSIX based interrupt.
138  */
139 int
140 bfad_setup_intr(struct bfad_s *bfad)
141 {
142         int error = 0;
143         u32 mask = 0, i, num_bit = 0, max_bit = 0;
144         struct msix_entry msix_entries[MAX_MSIX_ENTRY];
145
146         /* Call BFA to get the msix map for this PCI function.  */
147         bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit);
148
149         /* Set up the msix entry table */
150         bfad_init_msix_entry(bfad, msix_entries, mask, max_bit);
151
152         if (!msix_disable) {
153                 error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec);
154                 if (error) {
155                         /*
156                          * Only error number of vector is available.
157                          * We don't have a mechanism to map multiple
158                          * interrupts into one vector, so even if we
159                          * can try to request less vectors, we don't
160                          * know how to associate interrupt events to
161                          *  vectors. Linux doesn't dupicate vectors
162                          * in the MSIX table for this case.
163                          */
164
165                         printk(KERN_WARNING "bfad%d: "
166                                 "pci_enable_msix failed (%d),"
167                                 " use line based.\n", bfad->inst_no, error);
168
169                         goto line_based;
170                 }
171
172                 /* Save the vectors */
173                 for (i = 0; i < bfad->nvec; i++) {
174                         bfa_trc(bfad, msix_entries[i].vector);
175                         bfad->msix_tab[i].msix.vector = msix_entries[i].vector;
176                 }
177
178                 bfa_msix_init(&bfad->bfa, bfad->nvec);
179
180                 bfad->bfad_flags |= BFAD_MSIX_ON;
181
182                 return error;
183         }
184
185 line_based:
186         error = 0;
187         if (request_irq
188             (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS,
189              BFAD_DRIVER_NAME, bfad) != 0) {
190                 /* Enable interrupt handler failed */
191                 return 1;
192         }
193
194         return error;
195 }
196
197 void
198 bfad_remove_intr(struct bfad_s *bfad)
199 {
200         int             i;
201
202         if (bfad->bfad_flags & BFAD_MSIX_ON) {
203                 for (i = 0; i < bfad->nvec; i++)
204                         free_irq(bfad->msix_tab[i].msix.vector,
205                                         &bfad->msix_tab[i]);
206
207                 pci_disable_msix(bfad->pcidev);
208                 bfad->bfad_flags &= ~BFAD_MSIX_ON;
209         } else {
210                 free_irq(bfad->pcidev->irq, bfad);
211         }
212 }
213
214