Merge branch 'reiserfs/kill-bkl' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / scsi / bfa / bfad_intr.c
CommitLineData
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
21BFA_TRC_FILE(LDRV, INTR);
22
23/**
24 * bfa_isr BFA driver interrupt functions
25 */
7725ccfd
JH
26static int msix_disable;
27module_param(msix_disable, int, S_IRUGO | S_IWUSR);
28/**
29 * Line based interrupt handler.
30 */
f8ceafde 31static irqreturn_t
7725ccfd
JH
32bfad_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
62static irqreturn_t
63bfad_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 */
90static void
91bfad_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
110int
111bfad_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 */
138int
139bfad_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
184line_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
196void
197bfad_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