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