| 
									
										
										
										
											2009-09-23 17:46:15 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. | 
					
						
							|  |  |  |  * All rights reserved | 
					
						
							|  |  |  |  * www.brocade.com | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Linux driver for Brocade Fibre Channel Host Bus Adapter. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License (GPL) Version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  * General Public License for more details. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "bfad_drv.h"
 | 
					
						
							|  |  |  | #include "bfad_trcmod.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BFA_TRC_FILE(LDRV, INTR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  *  bfa_isr BFA driver interrupt functions | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2010-03-03 17:42:11 -08:00
										 |  |  | static int msix_disable_cb; | 
					
						
							|  |  |  | static int msix_disable_ct; | 
					
						
							|  |  |  | module_param(msix_disable_cb, int, S_IRUGO | S_IWUSR); | 
					
						
							| 
									
										
										
										
											2010-07-08 19:59:49 -07:00
										 |  |  | MODULE_PARM_DESC(msix_disable_cb, "Disable MSIX for Brocade-415/425/815/825" | 
					
						
							|  |  |  | 		" cards, default=0, Range[false:0|true:1]"); | 
					
						
							| 
									
										
										
										
											2010-03-03 17:42:11 -08:00
										 |  |  | module_param(msix_disable_ct, int, S_IRUGO | S_IWUSR); | 
					
						
							| 
									
										
										
										
											2010-07-08 19:59:49 -07:00
										 |  |  | MODULE_PARM_DESC(msix_disable_ct, "Disable MSIX for Brocade-1010/1020/804" | 
					
						
							|  |  |  | 		" cards, default=0, Range[false:0|true:1]"); | 
					
						
							| 
									
										
										
										
											2009-09-23 17:46:15 -07:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Line based interrupt handler. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2009-09-25 12:29:54 -07:00
										 |  |  | static irqreturn_t | 
					
						
							| 
									
										
										
										
											2009-09-23 17:46:15 -07:00
										 |  |  | bfad_intx(int irq, void *dev_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct bfad_s         *bfad = dev_id; | 
					
						
							|  |  |  | 	struct list_head         doneq; | 
					
						
							|  |  |  | 	unsigned long   flags; | 
					
						
							|  |  |  | 	bfa_boolean_t rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 	rc = bfa_intx(&bfad->bfa); | 
					
						
							|  |  |  | 	if (!rc) { | 
					
						
							|  |  |  | 		spin_unlock_irqrestore(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 		return IRQ_NONE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bfa_comp_deq(&bfad->bfa, &doneq); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!list_empty(&doneq)) { | 
					
						
							|  |  |  | 		bfa_comp_process(&bfad->bfa, &doneq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spin_lock_irqsave(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 		bfa_comp_free(&bfad->bfa, &doneq); | 
					
						
							|  |  |  | 		spin_unlock_irqrestore(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 		bfa_trc_fp(bfad, irq); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static irqreturn_t | 
					
						
							|  |  |  | bfad_msix(int irq, void *dev_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct bfad_msix_s *vec = dev_id; | 
					
						
							|  |  |  | 	struct bfad_s *bfad = vec->bfad; | 
					
						
							|  |  |  | 	struct list_head doneq; | 
					
						
							|  |  |  | 	unsigned long   flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bfa_msix(&bfad->bfa, vec->msix.entry); | 
					
						
							|  |  |  | 	bfa_comp_deq(&bfad->bfa, &doneq); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!list_empty(&doneq)) { | 
					
						
							|  |  |  | 		bfa_comp_process(&bfad->bfa, &doneq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spin_lock_irqsave(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 		bfa_comp_free(&bfad->bfa, &doneq); | 
					
						
							|  |  |  | 		spin_unlock_irqrestore(&bfad->bfad_lock, flags); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Initialize the MSIX entry table. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | bfad_init_msix_entry(struct bfad_s *bfad, struct msix_entry *msix_entries, | 
					
						
							|  |  |  | 			 int mask, int max_bit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int             i; | 
					
						
							|  |  |  | 	int             match = 0x00000001; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0, bfad->nvec = 0; i < MAX_MSIX_ENTRY; i++) { | 
					
						
							|  |  |  | 		if (mask & match) { | 
					
						
							|  |  |  | 			bfad->msix_tab[bfad->nvec].msix.entry = i; | 
					
						
							|  |  |  | 			bfad->msix_tab[bfad->nvec].bfad = bfad; | 
					
						
							|  |  |  | 			msix_entries[bfad->nvec].entry = i; | 
					
						
							|  |  |  | 			bfad->nvec++; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		match <<= 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | bfad_install_msix_handler(struct bfad_s *bfad) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int             i, error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < bfad->nvec; i++) { | 
					
						
							|  |  |  | 		error = request_irq(bfad->msix_tab[i].msix.vector, | 
					
						
							|  |  |  | 				    (irq_handler_t) bfad_msix, 0, | 
					
						
							|  |  |  | 				    BFAD_DRIVER_NAME, &bfad->msix_tab[i]); | 
					
						
							|  |  |  | 		bfa_trc(bfad, i); | 
					
						
							|  |  |  | 		bfa_trc(bfad, bfad->msix_tab[i].msix.vector); | 
					
						
							|  |  |  | 		if (error) { | 
					
						
							|  |  |  | 			int             j; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for (j = 0; j < i; j++) | 
					
						
							|  |  |  | 				free_irq(bfad->msix_tab[j].msix.vector, | 
					
						
							|  |  |  | 						&bfad->msix_tab[j]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Setup MSIX based interrupt. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | bfad_setup_intr(struct bfad_s *bfad) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 	u32 mask = 0, i, num_bit = 0, max_bit = 0; | 
					
						
							|  |  |  | 	struct msix_entry msix_entries[MAX_MSIX_ENTRY]; | 
					
						
							| 
									
										
										
										
											2010-03-03 17:42:11 -08:00
										 |  |  | 	struct pci_dev *pdev = bfad->pcidev; | 
					
						
							| 
									
										
										
										
											2009-09-23 17:46:15 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Call BFA to get the msix map for this PCI function.  */ | 
					
						
							|  |  |  | 	bfa_msix_getvecs(&bfad->bfa, &mask, &num_bit, &max_bit); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up the msix entry table */ | 
					
						
							|  |  |  | 	bfad_init_msix_entry(bfad, msix_entries, mask, max_bit); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-08 19:45:20 -07:00
										 |  |  | 	if ((bfa_asic_id_ct(pdev->device) && !msix_disable_ct) || | 
					
						
							|  |  |  | 		(!bfa_asic_id_ct(pdev->device) && !msix_disable_cb)) { | 
					
						
							| 
									
										
										
										
											2010-03-03 17:42:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-23 17:46:15 -07:00
										 |  |  | 		error = pci_enable_msix(bfad->pcidev, msix_entries, bfad->nvec); | 
					
						
							|  |  |  | 		if (error) { | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Only error number of vector is available. | 
					
						
							|  |  |  | 			 * We don't have a mechanism to map multiple | 
					
						
							|  |  |  | 			 * interrupts into one vector, so even if we | 
					
						
							|  |  |  | 			 * can try to request less vectors, we don't | 
					
						
							|  |  |  | 			 * know how to associate interrupt events to | 
					
						
							|  |  |  | 			 *  vectors. Linux doesn't dupicate vectors | 
					
						
							|  |  |  | 			 * in the MSIX table for this case. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			printk(KERN_WARNING "bfad%d: " | 
					
						
							|  |  |  | 				"pci_enable_msix failed (%d)," | 
					
						
							|  |  |  | 				" use line based.\n", bfad->inst_no, error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			goto line_based; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Save the vectors */ | 
					
						
							|  |  |  | 		for (i = 0; i < bfad->nvec; i++) { | 
					
						
							|  |  |  | 			bfa_trc(bfad, msix_entries[i].vector); | 
					
						
							|  |  |  | 			bfad->msix_tab[i].msix.vector = msix_entries[i].vector; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bfa_msix_init(&bfad->bfa, bfad->nvec); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bfad->bfad_flags |= BFAD_MSIX_ON; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | line_based: | 
					
						
							|  |  |  | 	error = 0; | 
					
						
							|  |  |  | 	if (request_irq | 
					
						
							|  |  |  | 	    (bfad->pcidev->irq, (irq_handler_t) bfad_intx, BFAD_IRQ_FLAGS, | 
					
						
							|  |  |  | 	     BFAD_DRIVER_NAME, bfad) != 0) { | 
					
						
							|  |  |  | 		/* Enable interrupt handler failed */ | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | bfad_remove_intr(struct bfad_s *bfad) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int             i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (bfad->bfad_flags & BFAD_MSIX_ON) { | 
					
						
							|  |  |  | 		for (i = 0; i < bfad->nvec; i++) | 
					
						
							|  |  |  | 			free_irq(bfad->msix_tab[i].msix.vector, | 
					
						
							|  |  |  | 					&bfad->msix_tab[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		pci_disable_msix(bfad->pcidev); | 
					
						
							|  |  |  | 		bfad->bfad_flags &= ~BFAD_MSIX_ON; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		free_irq(bfad->pcidev->irq, bfad); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |