RT-AC56 3.0.0.4.374.37 core
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / scsi / bfa / bfad_intr.c
blob90af419550b78a8436aef88549d1f043f492eb78
1 /*
2 * Copyright (c) 2005-2009 Brocade Communications Systems, Inc.
3 * All rights reserved
4 * www.brocade.com
6 * Linux driver for Brocade Fibre Channel Host Bus Adapter.
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
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.
18 #include "bfad_drv.h"
19 #include "bfad_trcmod.h"
21 BFA_TRC_FILE(LDRV, INTR);
23 /**
24 * bfa_isr BFA driver interrupt functions
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.
37 static irqreturn_t
38 bfad_intx(int irq, void *dev_id)
40 struct bfad_s *bfad = dev_id;
41 struct list_head doneq;
42 unsigned long flags;
43 bfa_boolean_t rc;
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;
52 bfa_comp_deq(&bfad->bfa, &doneq);
53 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
55 if (!list_empty(&doneq)) {
56 bfa_comp_process(&bfad->bfa, &doneq);
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);
64 return IRQ_HANDLED;
68 static irqreturn_t
69 bfad_msix(int irq, void *dev_id)
71 struct bfad_msix_s *vec = dev_id;
72 struct bfad_s *bfad = vec->bfad;
73 struct list_head doneq;
74 unsigned long flags;
76 spin_lock_irqsave(&bfad->bfad_lock, flags);
78 bfa_msix(&bfad->bfa, vec->msix.entry);
79 bfa_comp_deq(&bfad->bfa, &doneq);
80 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
82 if (!list_empty(&doneq)) {
83 bfa_comp_process(&bfad->bfa, &doneq);
85 spin_lock_irqsave(&bfad->bfad_lock, flags);
86 bfa_comp_free(&bfad->bfa, &doneq);
87 spin_unlock_irqrestore(&bfad->bfad_lock, flags);
90 return IRQ_HANDLED;
93 /**
94 * Initialize the MSIX entry table.
96 static void
97 bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries,
98 int mask, int max_bit)
100 int i;
101 int match = 0x00000001;
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++;
111 match <<= 1;
117 bfad_install_msix_handler(struct bfad_s *bfad)
119 int i, error = 0;
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;
130 for (j = 0; j < i; j++)
131 free_irq(bfad->msix_tab[j].msix.vector,
132 &bfad->msix_tab[j]);
134 return 1;
138 return 0;
142 * Setup MSIX based interrupt.
145 bfad_setup_intr(struct bfad_s *bfad)
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;
152 /* Call BFA to get the msix map for this PCI function. */
153 bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit);
155 /* Set up the msix entry table */
156 bfad_init_msix_entry(bfad, msix_entries, mask, max_bit);
158 if ((bfa_asic_id_ct(pdev->device) && !msix_disable_ct) ||
159 (!bfa_asic_id_ct(pdev->device) && !msix_disable_cb)) {
161 error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec);
162 if (error) {
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.
173 printk(KERN_WARNING "bfad%d: "
174 "pci_enable_msix failed (%d),"
175 " use line based.\n", bfad->inst_no, error);
177 goto line_based;
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;
186 bfa_msix_init(&bfad->bfa, bfad->nvec);
188 bfad->bfad_flags |= BFAD_MSIX_ON;
190 return error;
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;
202 return error;
205 void
206 bfad_remove_intr(struct bfad_s *bfad)
208 int i;
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]);
215 pci_disable_msix(bfad->pcidev);
216 bfad->bfad_flags &= ~BFAD_MSIX_ON;
217 } else {
218 free_irq(bfad->pcidev->irq, bfad);