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 */