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