1 /* 2 * Copyright 2008 Cisco Systems, Inc. All rights reserved. 3 * Copyright 2007 Nuova Systems, Inc. All rights reserved. 4 * 5 * This program is free software; you may redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; version 2 of the License. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 10 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 12 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 13 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 14 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 16 * SOFTWARE. 17 */ 18 #include <linux/string.h> 19 #include <linux/errno.h> 20 #include <linux/pci.h> 21 #include <linux/interrupt.h> 22 #include <scsi/libfc.h> 23 #include <scsi/fc_frame.h> 24 #include "vnic_dev.h" 25 #include "vnic_intr.h" 26 #include "vnic_stats.h" 27 #include "fnic_io.h" 28 #include "fnic.h" 29 30 static irqreturn_t fnic_isr_legacy(int irq, void *data) 31 { 32 struct fnic *fnic = data; 33 u32 pba; 34 unsigned long work_done = 0; 35 36 pba = vnic_intr_legacy_pba(fnic->legacy_pba); 37 if (!pba) 38 return IRQ_NONE; 39 40 if (pba & (1 << FNIC_INTX_NOTIFY)) { 41 vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]); 42 fnic_handle_link_event(fnic); 43 } 44 45 if (pba & (1 << FNIC_INTX_ERR)) { 46 vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_ERR]); 47 fnic_log_q_error(fnic); 48 } 49 50 if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) { 51 work_done += fnic_wq_copy_cmpl_handler(fnic, -1); 52 work_done += fnic_wq_cmpl_handler(fnic, -1); 53 work_done += fnic_rq_cmpl_handler(fnic, -1); 54 55 vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ], 56 work_done, 57 1 /* unmask intr */, 58 1 /* reset intr timer */); 59 } 60 61 return IRQ_HANDLED; 62 } 63 64 static irqreturn_t fnic_isr_msi(int irq, void *data) 65 { 66 struct fnic *fnic = data; 67 unsigned long work_done = 0; 68 69 work_done += fnic_wq_copy_cmpl_handler(fnic, -1); 70 work_done += fnic_wq_cmpl_handler(fnic, -1); 71 work_done += fnic_rq_cmpl_handler(fnic, -1); 72 73 vnic_intr_return_credits(&fnic->intr[0], 74 work_done, 75 1 /* unmask intr */, 76 1 /* reset intr timer */); 77 78 return IRQ_HANDLED; 79 } 80 81 static irqreturn_t fnic_isr_msix_rq(int irq, void *data) 82 { 83 struct fnic *fnic = data; 84 unsigned long rq_work_done = 0; 85 86 rq_work_done = fnic_rq_cmpl_handler(fnic, -1); 87 vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ], 88 rq_work_done, 89 1 /* unmask intr */, 90 1 /* reset intr timer */); 91 92 return IRQ_HANDLED; 93 } 94 95 static irqreturn_t fnic_isr_msix_wq(int irq, void *data) 96 { 97 struct fnic *fnic = data; 98 unsigned long wq_work_done = 0; 99 100 wq_work_done = fnic_wq_cmpl_handler(fnic, -1); 101 vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ], 102 wq_work_done, 103 1 /* unmask intr */, 104 1 /* reset intr timer */); 105 return IRQ_HANDLED; 106 } 107 108 static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data) 109 { 110 struct fnic *fnic = data; 111 unsigned long wq_copy_work_done = 0; 112 113 wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1); 114 vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY], 115 wq_copy_work_done, 116 1 /* unmask intr */, 117 1 /* reset intr timer */); 118 return IRQ_HANDLED; 119 } 120 121 static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data) 122 { 123 struct fnic *fnic = data; 124 125 vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]); 126 fnic_log_q_error(fnic); 127 fnic_handle_link_event(fnic); 128 129 return IRQ_HANDLED; 130 } 131 132 void fnic_free_intr(struct fnic *fnic) 133 { 134 int i; 135 136 switch (vnic_dev_get_intr_mode(fnic->vdev)) { 137 case VNIC_DEV_INTR_MODE_INTX: 138 case VNIC_DEV_INTR_MODE_MSI: 139 free_irq(fnic->pdev->irq, fnic); 140 break; 141 142 case VNIC_DEV_INTR_MODE_MSIX: 143 for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) 144 if (fnic->msix[i].requested) 145 free_irq(fnic->msix_entry[i].vector, 146 fnic->msix[i].devid); 147 break; 148 149 default: 150 break; 151 } 152 } 153 154 int fnic_request_intr(struct fnic *fnic) 155 { 156 int err = 0; 157 int i; 158 159 switch (vnic_dev_get_intr_mode(fnic->vdev)) { 160 161 case VNIC_DEV_INTR_MODE_INTX: 162 err = request_irq(fnic->pdev->irq, &fnic_isr_legacy, 163 IRQF_SHARED, DRV_NAME, fnic); 164 break; 165 166 case VNIC_DEV_INTR_MODE_MSI: 167 err = request_irq(fnic->pdev->irq, &fnic_isr_msi, 168 0, fnic->name, fnic); 169 break; 170 171 case VNIC_DEV_INTR_MODE_MSIX: 172 173 sprintf(fnic->msix[FNIC_MSIX_RQ].devname, 174 "%.11s-fcs-rq", fnic->name); 175 fnic->msix[FNIC_MSIX_RQ].isr = fnic_isr_msix_rq; 176 fnic->msix[FNIC_MSIX_RQ].devid = fnic; 177 178 sprintf(fnic->msix[FNIC_MSIX_WQ].devname, 179 "%.11s-fcs-wq", fnic->name); 180 fnic->msix[FNIC_MSIX_WQ].isr = fnic_isr_msix_wq; 181 fnic->msix[FNIC_MSIX_WQ].devid = fnic; 182 183 sprintf(fnic->msix[FNIC_MSIX_WQ_COPY].devname, 184 "%.11s-scsi-wq", fnic->name); 185 fnic->msix[FNIC_MSIX_WQ_COPY].isr = fnic_isr_msix_wq_copy; 186 fnic->msix[FNIC_MSIX_WQ_COPY].devid = fnic; 187 188 sprintf(fnic->msix[FNIC_MSIX_ERR_NOTIFY].devname, 189 "%.11s-err-notify", fnic->name); 190 fnic->msix[FNIC_MSIX_ERR_NOTIFY].isr = 191 fnic_isr_msix_err_notify; 192 fnic->msix[FNIC_MSIX_ERR_NOTIFY].devid = fnic; 193 194 for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) { 195 err = request_irq(fnic->msix_entry[i].vector, 196 fnic->msix[i].isr, 0, 197 fnic->msix[i].devname, 198 fnic->msix[i].devid); 199 if (err) { 200 shost_printk(KERN_ERR, fnic->lport->host, 201 "MSIX: request_irq" 202 " failed %d\n", err); 203 fnic_free_intr(fnic); 204 break; 205 } 206 fnic->msix[i].requested = 1; 207 } 208 break; 209 210 default: 211 break; 212 } 213 214 return err; 215 } 216 217 int fnic_set_intr_mode(struct fnic *fnic) 218 { 219 unsigned int n = ARRAY_SIZE(fnic->rq); 220 unsigned int m = ARRAY_SIZE(fnic->wq); 221 unsigned int o = ARRAY_SIZE(fnic->wq_copy); 222 unsigned int i; 223 224 /* 225 * Set interrupt mode (INTx, MSI, MSI-X) depending 226 * system capabilities. 227 * 228 * Try MSI-X first 229 * 230 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs 231 * (last INTR is used for WQ/RQ errors and notification area) 232 */ 233 234 BUG_ON(ARRAY_SIZE(fnic->msix_entry) < n + m + o + 1); 235 for (i = 0; i < n + m + o + 1; i++) 236 fnic->msix_entry[i].entry = i; 237 238 if (fnic->rq_count >= n && 239 fnic->raw_wq_count >= m && 240 fnic->wq_copy_count >= o && 241 fnic->cq_count >= n + m + o) { 242 if (!pci_enable_msix(fnic->pdev, fnic->msix_entry, 243 n + m + o + 1)) { 244 fnic->rq_count = n; 245 fnic->raw_wq_count = m; 246 fnic->wq_copy_count = o; 247 fnic->wq_count = m + o; 248 fnic->cq_count = n + m + o; 249 fnic->intr_count = n + m + o + 1; 250 fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY; 251 252 FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, 253 "Using MSI-X Interrupts\n"); 254 vnic_dev_set_intr_mode(fnic->vdev, 255 VNIC_DEV_INTR_MODE_MSIX); 256 return 0; 257 } 258 } 259 260 /* 261 * Next try MSI 262 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR 263 */ 264 if (fnic->rq_count >= 1 && 265 fnic->raw_wq_count >= 1 && 266 fnic->wq_copy_count >= 1 && 267 fnic->cq_count >= 3 && 268 fnic->intr_count >= 1 && 269 !pci_enable_msi(fnic->pdev)) { 270 271 fnic->rq_count = 1; 272 fnic->raw_wq_count = 1; 273 fnic->wq_copy_count = 1; 274 fnic->wq_count = 2; 275 fnic->cq_count = 3; 276 fnic->intr_count = 1; 277 fnic->err_intr_offset = 0; 278 279 FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, 280 "Using MSI Interrupts\n"); 281 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI); 282 283 return 0; 284 } 285 286 /* 287 * Next try INTx 288 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs 289 * 1 INTR is used for all 3 queues, 1 INTR for queue errors 290 * 1 INTR for notification area 291 */ 292 293 if (fnic->rq_count >= 1 && 294 fnic->raw_wq_count >= 1 && 295 fnic->wq_copy_count >= 1 && 296 fnic->cq_count >= 3 && 297 fnic->intr_count >= 3) { 298 299 fnic->rq_count = 1; 300 fnic->raw_wq_count = 1; 301 fnic->wq_copy_count = 1; 302 fnic->cq_count = 3; 303 fnic->intr_count = 3; 304 305 FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, 306 "Using Legacy Interrupts\n"); 307 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); 308 309 return 0; 310 } 311 312 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 313 314 return -EINVAL; 315 } 316 317 void fnic_clear_intr_mode(struct fnic *fnic) 318 { 319 switch (vnic_dev_get_intr_mode(fnic->vdev)) { 320 case VNIC_DEV_INTR_MODE_MSIX: 321 pci_disable_msix(fnic->pdev); 322 break; 323 case VNIC_DEV_INTR_MODE_MSI: 324 pci_disable_msi(fnic->pdev); 325 break; 326 default: 327 break; 328 } 329 330 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); 331 } 332 333