• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/scsi/bfa/
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 */
26static int msix_disable_cb;
27static int msix_disable_ct;
28module_param(msix_disable_cb, int, S_IRUGO | S_IWUSR);
29MODULE_PARM_DESC(msix_disable_cb, "Disable MSIX for Brocade-415/425/815/825"
30		" cards, default=0, Range[false:0|true:1]");
31module_param(msix_disable_ct, int, S_IRUGO | S_IWUSR);
32MODULE_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.
36 */
37static irqreturn_t
38bfad_intx(int irq, void *dev_id)
39{
40	struct bfad_s         *bfad = dev_id;
41	struct list_head         doneq;
42	unsigned long   flags;
43	bfa_boolean_t rc;
44
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;
50	}
51
52	bfa_comp_deq(&bfad->bfa, &doneq);
53	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
54
55	if (!list_empty(&doneq)) {
56		bfa_comp_process(&bfad->bfa, &doneq);
57
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);
62	}
63
64	return IRQ_HANDLED;
65
66}
67
68static irqreturn_t
69bfad_msix(int irq, void *dev_id)
70{
71	struct bfad_msix_s *vec = dev_id;
72	struct bfad_s *bfad = vec->bfad;
73	struct list_head doneq;
74	unsigned long   flags;
75
76	spin_lock_irqsave(&bfad->bfad_lock, flags);
77
78	bfa_msix(&bfad->bfa, vec->msix.entry);
79	bfa_comp_deq(&bfad->bfa, &doneq);
80	spin_unlock_irqrestore(&bfad->bfad_lock, flags);
81
82	if (!list_empty(&doneq)) {
83		bfa_comp_process(&bfad->bfa, &doneq);
84
85		spin_lock_irqsave(&bfad->bfad_lock, flags);
86		bfa_comp_free(&bfad->bfa, &doneq);
87		spin_unlock_irqrestore(&bfad->bfad_lock, flags);
88	}
89
90	return IRQ_HANDLED;
91}
92
93/**
94 * Initialize the MSIX entry table.
95 */
96static void
97bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries,
98			 int mask, int max_bit)
99{
100	int             i;
101	int             match = 0x00000001;
102
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++;
109		}
110
111		match <<= 1;
112	}
113
114}
115
116int
117bfad_install_msix_handler(struct bfad_s *bfad)
118{
119	int             i, error = 0;
120
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;
129
130			for (j = 0; j < i; j++)
131				free_irq(bfad->msix_tab[j].msix.vector,
132						&bfad->msix_tab[j]);
133
134			return 1;
135		}
136	}
137
138	return 0;
139}
140
141/**
142 * Setup MSIX based interrupt.
143 */
144int
145bfad_setup_intr(struct bfad_s *bfad)
146{
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;
151
152	/* Call BFA to get the msix map for this PCI function.  */
153	bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit);
154
155	/* Set up the msix entry table */
156	bfad_init_msix_entry(bfad, msix_entries, mask, max_bit);
157
158	if ((bfa_asic_id_ct(pdev->device) && !msix_disable_ct) ||
159		(!bfa_asic_id_ct(pdev->device) && !msix_disable_cb)) {
160
161		error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec);
162		if (error) {
163			/*
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.
171			 */
172
173			printk(KERN_WARNING "bfad%d: "
174				"pci_enable_msix failed (%d),"
175				" use line based.\n", bfad->inst_no, error);
176
177			goto line_based;
178		}
179
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;
184		}
185
186		bfa_msix_init(&bfad->bfa, bfad->nvec);
187
188		bfad->bfad_flags |= BFAD_MSIX_ON;
189
190		return error;
191	}
192
193line_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;
200	}
201
202	return error;
203}
204
205void
206bfad_remove_intr(struct bfad_s *bfad)
207{
208	int             i;
209
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]);
214
215		pci_disable_msix(bfad->pcidev);
216		bfad->bfad_flags &= ~BFAD_MSIX_ON;
217	} else {
218		free_irq(bfad->pcidev->irq, bfad);
219	}
220}
221