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