1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2010 Emulex. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Source file interrupt registration 29 * and related helper functions 30 */ 31 32 #include <oce_impl.h> 33 34 static uint_t oce_isr(caddr_t arg1, caddr_t arg2); 35 36 /* 37 * top level function to setup interrupts 38 * 39 * dev - software handle to the device 40 * 41 * return DDI_SUCCESS => success, failure otherwise 42 */ 43 int 44 oce_setup_intr(struct oce_dev *dev) 45 { 46 int ret; 47 int intr_types = 0; 48 int navail = 0; 49 int nsupported = 0; 50 int min = 0; 51 int nreqd = 0; 52 int nallocd = 0; 53 54 /* get supported intr types */ 55 ret = ddi_intr_get_supported_types(dev->dip, &intr_types); 56 if (ret != DDI_SUCCESS) { 57 oce_log(dev, CE_WARN, MOD_CONFIG, "%s", 58 "Failed to retrieve intr types "); 59 return (DDI_FAILURE); 60 } 61 62 retry_intr: 63 if (intr_types & DDI_INTR_TYPE_MSIX) { 64 dev->intr_type = DDI_INTR_TYPE_MSIX; 65 /* one vector is shared by MCC and Tx */ 66 nreqd = dev->rx_rings + 1; 67 min = OCE_MIN_VECTORS; 68 } else if (intr_types & DDI_INTR_TYPE_FIXED) { 69 dev->intr_type = DDI_INTR_TYPE_FIXED; 70 nreqd = OCE_MIN_VECTORS; 71 min = OCE_MIN_VECTORS; 72 } 73 74 ret = ddi_intr_get_nintrs(dev->dip, dev->intr_type, &nsupported); 75 if (ret != DDI_SUCCESS) { 76 oce_log(dev, CE_WARN, MOD_CONFIG, 77 "Could not get nintrs:0x%d", ret); 78 return (DDI_FAILURE); 79 } 80 81 /* get the number of vectors available */ 82 ret = ddi_intr_get_navail(dev->dip, dev->intr_type, &navail); 83 if (ret != DDI_SUCCESS || navail < min) { 84 oce_log(dev, CE_WARN, MOD_CONFIG, 85 "Could not get msix vectors:0x%x", 86 navail); 87 return (DDI_FAILURE); 88 } 89 90 if (navail < min) { 91 return (DDI_FAILURE); 92 } 93 94 /* if the requested number is more than available reset reqd */ 95 if (navail < nreqd) { 96 nreqd = navail; 97 } 98 99 /* allocate htable */ 100 dev->hsize = nreqd * sizeof (ddi_intr_handle_t); 101 dev->htable = kmem_zalloc(dev->hsize, KM_NOSLEEP); 102 103 if (dev->htable == NULL) 104 return (DDI_FAILURE); 105 106 nallocd = 0; 107 /* allocate interrupt handlers */ 108 ret = ddi_intr_alloc(dev->dip, dev->htable, dev->intr_type, 109 0, nreqd, &nallocd, DDI_INTR_ALLOC_NORMAL); 110 111 if (ret != DDI_SUCCESS) { 112 goto fail_intr; 113 } 114 115 dev->num_vectors = nallocd; 116 if (nallocd < min) { 117 goto fail_intr; 118 } 119 120 /* 121 * get the interrupt priority. Assumption is that all handlers have 122 * equal priority 123 */ 124 125 ret = ddi_intr_get_pri(dev->htable[0], &dev->intr_pri); 126 127 if (ret != DDI_SUCCESS) { 128 goto fail_intr; 129 } 130 131 (void) ddi_intr_get_cap(dev->htable[0], &dev->intr_cap); 132 133 if ((intr_types & DDI_INTR_TYPE_MSIX) && (nallocd > 1)) { 134 dev->rx_rings = nallocd - 1; 135 } else { 136 dev->rx_rings = 1; 137 } 138 139 return (DDI_SUCCESS); 140 141 fail_intr: 142 (void) oce_teardown_intr(dev); 143 if ((dev->intr_type == DDI_INTR_TYPE_MSIX) && 144 (intr_types & DDI_INTR_TYPE_FIXED)) { 145 intr_types &= ~DDI_INTR_TYPE_MSIX; 146 oce_log(dev, CE_NOTE, MOD_CONFIG, "%s", 147 "Could not get MSIX vectors, trying for FIXED vectors"); 148 goto retry_intr; 149 } 150 return (DDI_FAILURE); 151 } 152 153 /* 154 * top level function to undo initialization in oce_setup_intr 155 * 156 * dev - software handle to the device 157 * 158 * return DDI_SUCCESS => success, failure otherwise 159 */ 160 int 161 oce_teardown_intr(struct oce_dev *dev) 162 { 163 int i; 164 165 /* release handlers */ 166 for (i = 0; i < dev->num_vectors; i++) { 167 (void) ddi_intr_free(dev->htable[i]); 168 } 169 170 /* release htable */ 171 kmem_free(dev->htable, dev->hsize); 172 dev->htable = NULL; 173 174 return (DDI_SUCCESS); 175 } 176 177 /* 178 * helper function to add ISR based on interrupt type 179 * 180 * dev - software handle to the device 181 * 182 * return DDI_SUCCESS => success, failure otherwise 183 */ 184 int 185 oce_setup_handlers(struct oce_dev *dev) 186 { 187 int i = 0; 188 int ret; 189 for (i = 0; i < dev->num_vectors; i++) { 190 ret = ddi_intr_add_handler(dev->htable[i], oce_isr, 191 (caddr_t)dev->eq[i], NULL); 192 if (ret != DDI_SUCCESS) { 193 oce_log(dev, CE_WARN, MOD_CONFIG, "%s", 194 "Failed to add interrupt handlers"); 195 for (i--; i >= 0; i--) { 196 (void) ddi_intr_remove_handler(dev->htable[i]); 197 } 198 return (DDI_FAILURE); 199 } 200 } 201 return (DDI_SUCCESS); 202 } 203 204 /* 205 * helper function to remove ISRs added in oce_setup_handlers 206 * 207 * dev - software handle to the device 208 * 209 * return DDI_SUCCESS => success, failure otherwise 210 */ 211 void 212 oce_remove_handler(struct oce_dev *dev) 213 { 214 int nvec; 215 for (nvec = 0; nvec < dev->num_vectors; nvec++) { 216 (void) ddi_intr_remove_handler(dev->htable[nvec]); 217 } 218 } 219 220 void 221 oce_chip_ei(struct oce_dev *dev) 222 { 223 uint32_t reg; 224 225 reg = OCE_CFG_READ32(dev, PCICFG_INTR_CTRL); 226 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 227 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 228 } 229 reg |= HOSTINTR_MASK; 230 OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg); 231 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 232 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 233 } 234 } 235 236 /* 237 * function to enable interrupts 238 * 239 * dev - software handle to the device 240 * 241 * return DDI_SUCCESS => success, failure otherwise 242 */ 243 void 244 oce_ei(struct oce_dev *dev) 245 { 246 int i; 247 int ret; 248 249 if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) { 250 (void) ddi_intr_block_enable(dev->htable, dev->num_vectors); 251 } else { 252 253 for (i = 0; i < dev->num_vectors; i++) { 254 ret = ddi_intr_enable(dev->htable[i]); 255 if (ret != DDI_SUCCESS) { 256 for (i--; i >= 0; i--) { 257 (void) ddi_intr_disable(dev->htable[i]); 258 } 259 } 260 } 261 } 262 oce_chip_ei(dev); 263 } /* oce_ei */ 264 265 void 266 oce_chip_di(struct oce_dev *dev) 267 { 268 uint32_t reg; 269 270 reg = OCE_CFG_READ32(dev, PCICFG_INTR_CTRL); 271 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 272 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 273 } 274 reg &= ~HOSTINTR_MASK; 275 OCE_CFG_WRITE32(dev, PCICFG_INTR_CTRL, reg); 276 if (oce_fm_check_acc_handle(dev, dev->dev_cfg_handle) != DDI_FM_OK) { 277 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 278 } 279 } 280 281 /* 282 * function to disable interrupts 283 * 284 * dev - software handle to the device 285 * 286 * return DDI_SUCCESS => success, failure otherwise 287 */ 288 void 289 oce_di(struct oce_dev *dev) 290 { 291 int i; 292 int ret; 293 294 oce_chip_di(dev); 295 if (dev->intr_cap & DDI_INTR_FLAG_BLOCK) { 296 (void) ddi_intr_block_disable(dev->htable, dev->num_vectors); 297 } else { 298 for (i = 0; i < dev->num_vectors; i++) { 299 ret = ddi_intr_disable(dev->htable[i]); 300 if (ret != DDI_SUCCESS) { 301 oce_log(dev, CE_WARN, MOD_CONFIG, 302 "Failed to disable interrupts 0x%x", ret); 303 } 304 } 305 } 306 } /* oce_di */ 307 308 /* 309 * command interrupt handler routine added to all vectors 310 * 311 * arg1 = callback data 312 * arg2 - callback data 313 * 314 * return DDI_INTR_CLAIMED => interrupt was claimed by the ISR 315 */ 316 static uint_t 317 oce_isr(caddr_t arg1, caddr_t arg2) 318 { 319 struct oce_eq *eq; 320 struct oce_eqe *eqe; 321 uint16_t num_eqe = 0; 322 uint16_t cq_id; 323 struct oce_cq *cq; 324 struct oce_dev *dev; 325 326 _NOTE(ARGUNUSED(arg2)); 327 328 eq = (struct oce_eq *)(void *)(arg1); 329 330 dev = eq->parent; 331 332 eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 333 334 while (eqe->u0.dw0) { 335 336 eqe->u0.dw0 = LE_32(eqe->u0.dw0); 337 338 /* if not CQ then continue else flag an error */ 339 if (EQ_MAJOR_CODE_COMPLETION != eqe->u0.s.major_code) { 340 oce_log(dev, CE_WARN, MOD_ISR, 341 "NOT a CQ event. 0x%x", 342 eqe->u0.s.major_code); 343 } 344 345 /* get the cq from the eqe */ 346 cq_id = eqe->u0.s.resource_id % OCE_MAX_CQ; 347 cq = dev->cq[cq_id]; 348 349 /* Call the completion handler */ 350 (void) cq->cq_handler(cq->cb_arg); 351 352 /* clear valid bit and progress eqe */ 353 eqe->u0.dw0 = 0; 354 RING_GET(eq->ring, 1); 355 eqe = RING_GET_CONSUMER_ITEM_VA(eq->ring, struct oce_eqe); 356 num_eqe++; 357 } /* for all EQEs */ 358 359 /* ring the eq doorbell, signify that it's done processing */ 360 oce_arm_eq(dev, eq->eq_id, num_eqe, B_TRUE, B_TRUE); 361 if (num_eqe > 0) { 362 return (DDI_INTR_CLAIMED); 363 } else { 364 return (DDI_INTR_UNCLAIMED); 365 } 366 } /* oce_msix_handler */ 367