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