blob: f104e029cac960ba913f9627f25e4f5a918faf09 [file] [log] [blame]
Jing Huang7725ccf2009-09-23 17:46:15 -07001/*
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
21BFA_TRC_FILE(LDRV, INTR);
22
23/**
24 * bfa_isr BFA driver interrupt functions
25 */
26irqreturn_t bfad_intx(int irq, void *dev_id);
27static int msix_disable;
28module_param(msix_disable, int, S_IRUGO | S_IWUSR);
29/**
30 * Line based interrupt handler.
31 */
32irqreturn_t
33bfad_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
63static irqreturn_t
64bfad_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 */
91static void
92bfad_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
111int
112bfad_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 */
139int
140bfad_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
185line_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
197void
198bfad_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