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 fnic->fnic_stats.misc_stats.last_isr_time = jiffies; 41 atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); 42 43 if (pba & (1 << FNIC_INTX_NOTIFY)) { 44 vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]); 45 fnic_handle_link_event(fnic); 46 } 47 48 if (pba & (1 << FNIC_INTX_ERR)) { 49 vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_ERR]); 50 fnic_log_q_error(fnic); 51 } 52 53 if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) { 54 work_done += fnic_wq_copy_cmpl_handler(fnic, -1); 55 work_done += fnic_wq_cmpl_handler(fnic, -1); 56 work_done += fnic_rq_cmpl_handler(fnic, -1); 57 58 vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ], 59 work_done, 60 1 /* unmask intr */, 61 1 /* reset intr timer */); 62 } 63 64 return IRQ_HANDLED; 65 } 66 67 static irqreturn_t fnic_isr_msi(int irq, void *data) 68 { 69 struct fnic *fnic = data; 70 unsigned long work_done = 0; 71 72 fnic->fnic_stats.misc_stats.last_isr_time = jiffies; 73 atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); 74 75 work_done += fnic_wq_copy_cmpl_handler(fnic, -1); 76 work_done += fnic_wq_cmpl_handler(fnic, -1); 77 work_done += fnic_rq_cmpl_handler(fnic, -1); 78 79 vnic_intr_return_credits(&fnic->intr[0], 80 work_done, 81 1 /* unmask intr */, 82 1 /* reset intr timer */); 83 84 return IRQ_HANDLED; 85 } 86 87 static irqreturn_t fnic_isr_msix_rq(int irq, void *data) 88 { 89 struct fnic *fnic = data; 90 unsigned long rq_work_done = 0; 91 92 fnic->fnic_stats.misc_stats.last_isr_time = jiffies; 93 atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); 94 95 rq_work_done = fnic_rq_cmpl_handler(fnic, -1); 96 vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ], 97 rq_work_done, 98 1 /* unmask intr */, 99 1 /* reset intr timer */); 100 101 return IRQ_HANDLED; 102 } 103 104 static irqreturn_t fnic_isr_msix_wq(int irq, void *data) 105 { 106 struct fnic *fnic = data; 107 unsigned long wq_work_done = 0; 108 109 fnic->fnic_stats.misc_stats.last_isr_time = jiffies; 110 atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); 111 112 wq_work_done = fnic_wq_cmpl_handler(fnic, -1); 113 vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ], 114 wq_work_done, 115 1 /* unmask intr */, 116 1 /* reset intr timer */); 117 return IRQ_HANDLED; 118 } 119 120 static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data) 121 { 122 struct fnic *fnic = data; 123 unsigned long wq_copy_work_done = 0; 124 125 fnic->fnic_stats.misc_stats.last_isr_time = jiffies; 126 atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); 127 128 wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1); 129 vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY], 130 wq_copy_work_done, 131 1 /* unmask intr */, 132 1 /* reset intr timer */); 133 return IRQ_HANDLED; 134 } 135 136 static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data) 137 { 138 struct fnic *fnic = data; 139 140 fnic->fnic_stats.misc_stats.last_isr_time = jiffies; 141 atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); 142 143 vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]); 144 fnic_log_q_error(fnic); 145 fnic_handle_link_event(fnic); 146 147 return IRQ_HANDLED; 148 } 149 150 void fnic_free_intr(struct fnic *fnic) 151 { 152 int i; 153 154 switch (vnic_dev_get_intr_mode(fnic->vdev)) { 155 case VNIC_DEV_INTR_MODE_INTX: 156 case VNIC_DEV_INTR_MODE_MSI: 157 free_irq(fnic->pdev->irq, fnic); 158 break; 159 160 case VNIC_DEV_INTR_MODE_MSIX: 161 for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) 162 if (fnic->msix[i].requested) 163 free_irq(fnic->msix_entry[i].vector, 164 fnic->msix[i].devid); 165 break; 166 167 default: 168 break; 169 } 170 } 171 172 int fnic_request_intr(struct fnic *fnic) 173 { 174 int err = 0; 175 int i; 176 177 switch (vnic_dev_get_intr_mode(fnic->vdev)) { 178 179 case VNIC_DEV_INTR_MODE_INTX: 180 err = request_irq(fnic->pdev->irq, &fnic_isr_legacy, 181 IRQF_SHARED, DRV_NAME, fnic); 182 break; 183 184 case VNIC_DEV_INTR_MODE_MSI: 185 err = request_irq(fnic->pdev->irq, &fnic_isr_msi, 186 0, fnic->name, fnic); 187 break; 188 189 case VNIC_DEV_INTR_MODE_MSIX: 190 191 sprintf(fnic->msix[FNIC_MSIX_RQ].devname, 192 "%.11s-fcs-rq", fnic->name); 193 fnic->msix[FNIC_MSIX_RQ].isr = fnic_isr_msix_rq; 194 fnic->msix[FNIC_MSIX_RQ].devid = fnic; 195 196 sprintf(fnic->msix[FNIC_MSIX_WQ].devname, 197 "%.11s-fcs-wq", fnic->name); 198 fnic->msix[FNIC_MSIX_WQ].isr = fnic_isr_msix_wq; 199 fnic->msix[FNIC_MSIX_WQ].devid = fnic; 200 201 sprintf(fnic->msix[FNIC_MSIX_WQ_COPY].devname, 202 "%.11s-scsi-wq", fnic->name); 203 fnic->msix[FNIC_MSIX_WQ_COPY].isr = fnic_isr_msix_wq_copy; 204 fnic->msix[FNIC_MSIX_WQ_COPY].devid = fnic; 205 206 sprintf(fnic->msix[FNIC_MSIX_ERR_NOTIFY].devname, 207 "%.11s-err-notify", fnic->name); 208 fnic->msix[FNIC_MSIX_ERR_NOTIFY].isr = 209 fnic_isr_msix_err_notify; 210 fnic->msix[FNIC_MSIX_ERR_NOTIFY].devid = fnic; 211 212 for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) { 213 err = request_irq(fnic->msix_entry[i].vector, 214 fnic->msix[i].isr, 0, 215 fnic->msix[i].devname, 216 fnic->msix[i].devid); 217 if (err) { 218 shost_printk(KERN_ERR, fnic->lport->host, 219 "MSIX: request_irq" 220 " failed %d\n", err); 221 fnic_free_intr(fnic); 222 break; 223 } 224 fnic->msix[i].requested = 1; 225 } 226 break; 227 228 default: 229 break; 230 } 231 232 return err; 233 } 234 235 int fnic_set_intr_mode(struct fnic *fnic) 236 { 237 unsigned int n = ARRAY_SIZE(fnic->rq); 238 unsigned int m = ARRAY_SIZE(fnic->wq); 239 unsigned int o = ARRAY_SIZE(fnic->wq_copy); 240 unsigned int i; 241 242 /* 243 * Set interrupt mode (INTx, MSI, MSI-X) depending 244 * system capabilities. 245 * 246 * Try MSI-X first 247 * 248 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs 249 * (last INTR is used for WQ/RQ errors and notification area) 250 */ 251 252 BUG_ON(ARRAY_SIZE(fnic->msix_entry) < n + m + o + 1); 253 for (i = 0; i < n + m + o + 1; i++) 254 fnic->msix_entry[i].entry = i; 255 256 if (fnic->rq_count >= n && 257 fnic->raw_wq_count >= m && 258 fnic->wq_copy_count >= o && 259 fnic->cq_count >= n + m + o) { 260 if (!pci_enable_msix_exact(fnic->pdev, fnic->msix_entry, 261 n + m + o + 1)) { 262 fnic->rq_count = n; 263 fnic->raw_wq_count = m; 264 fnic->wq_copy_count = o; 265 fnic->wq_count = m + o; 266 fnic->cq_count = n + m + o; 267 fnic->intr_count = n + m + o + 1; 268 fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY; 269 270 FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, 271 "Using MSI-X Interrupts\n"); 272 vnic_dev_set_intr_mode(fnic->vdev, 273 VNIC_DEV_INTR_MODE_MSIX); 274 return 0; 275 } 276 } 277 278 /* 279 * Next try MSI 280 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR 281 */ 282 if (fnic->rq_count >= 1 && 283 fnic->raw_wq_count >= 1 && 284 fnic->wq_copy_count >= 1 && 285 fnic->cq_count >= 3 && 286 fnic->intr_count >= 1 && 287 !pci_enable_msi(fnic->pdev)) { 288 289 fnic->rq_count = 1; 290 fnic->raw_wq_count = 1; 291 fnic->wq_copy_count = 1; 292 fnic->wq_count = 2; 293 fnic->cq_count = 3; 294 fnic->intr_count = 1; 295 fnic->err_intr_offset = 0; 296 297 FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, 298 "Using MSI Interrupts\n"); 299 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI); 300 301 return 0; 302 } 303 304 /* 305 * Next try INTx 306 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs 307 * 1 INTR is used for all 3 queues, 1 INTR for queue errors 308 * 1 INTR for notification area 309 */ 310 311 if (fnic->rq_count >= 1 && 312 fnic->raw_wq_count >= 1 && 313 fnic->wq_copy_count >= 1 && 314 fnic->cq_count >= 3 && 315 fnic->intr_count >= 3) { 316 317 fnic->rq_count = 1; 318 fnic->raw_wq_count = 1; 319 fnic->wq_copy_count = 1; 320 fnic->cq_count = 3; 321 fnic->intr_count = 3; 322 323 FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, 324 "Using Legacy Interrupts\n"); 325 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); 326 327 return 0; 328 } 329 330 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); 331 332 return -EINVAL; 333 } 334 335 void fnic_clear_intr_mode(struct fnic *fnic) 336 { 337 switch (vnic_dev_get_intr_mode(fnic->vdev)) { 338 case VNIC_DEV_INTR_MODE_MSIX: 339 pci_disable_msix(fnic->pdev); 340 break; 341 case VNIC_DEV_INTR_MODE_MSI: 342 pci_disable_msi(fnic->pdev); 343 break; 344 default: 345 break; 346 } 347 348 vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); 349 } 350 351