Commit | Line | Data |
---|---|---|
7725ccfd JH |
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 | */ | |
7725ccfd JH |
26 | static int msix_disable; |
27 | module_param(msix_disable, int, S_IRUGO | S_IWUSR); | |
28 | /** | |
29 | * Line based interrupt handler. | |
30 | */ | |
f8ceafde | 31 | static irqreturn_t |
7725ccfd JH |
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 |