blob: 2b7dbecbebcaddf2ddd1873e55f6c64adaaee662 [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 */
Krishna Gudipati4c147dd2010-03-03 17:42:11 -080026static int msix_disable_cb;
27static int msix_disable_ct;
28module_param(msix_disable_cb, int, S_IRUGO | S_IWUSR);
29module_param(msix_disable_ct, int, S_IRUGO | S_IWUSR);
Jing Huang7725ccf2009-09-23 17:46:15 -070030/**
31 * Line based interrupt handler.
32 */
Jing Huangf8ceafd2009-09-25 12:29:54 -070033static irqreturn_t
Jing Huang7725ccf2009-09-23 17:46:15 -070034bfad_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
64static irqreturn_t
65bfad_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 */
92static void
93bfad_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
112int
113bfad_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 */
140int
141bfad_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];
Krishna Gudipati4c147dd2010-03-03 17:42:11 -0800146 struct pci_dev *pdev = bfad->pcidev;
Jing Huang7725ccf2009-09-23 17:46:15 -0700147
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
Krishna Gudipati4c147dd2010-03-03 17:42:11 -0800154 if ((pdev->device == BFA_PCI_DEVICE_ID_CT && !msix_disable_ct) ||
155 (pdev->device != BFA_PCI_DEVICE_ID_CT && !msix_disable_cb)) {
156
Jing Huang7725ccf2009-09-23 17:46:15 -0700157 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
189line_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
201void
202bfad_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