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 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * nxge_intr.c 29 * 30 * This file manages the interrupts for a hybrid I/O (hio) device. 31 * In the future, it may manage interrupts for all Neptune-based 32 * devices. 33 * 34 */ 35 36 #include <sys/nxge/nxge_impl.h> 37 #include <sys/nxge/nxge_hio.h> 38 39 /* 40 * External prototypes 41 */ 42 43 /* The following function may be found in nxge_[t|r]xdma.c */ 44 extern uint_t nxge_tx_intr(char *, char *); 45 extern uint_t nxge_rx_intr(char *, char *); 46 47 /* 48 * Local prototypes 49 */ 50 static int nxge_intr_vec_find(nxge_t *, vpc_type_t, int); 51 52 /* 53 * nxge_intr_add 54 * 55 * Add <channel>'s interrupt. 56 * 57 * Arguments: 58 * nxge 59 * type Tx or Rx 60 * channel The channel whose interrupt we want to add. 61 * 62 * Notes: 63 * Add here means: add a handler, enable, & arm the interrupt. 64 * 65 * Context: 66 * Service domain 67 * 68 */ 69 nxge_status_t 70 nxge_intr_add( 71 nxge_t *nxge, 72 vpc_type_t type, 73 int channel) 74 { 75 nxge_intr_t *interrupts; /* The global interrupt data. */ 76 nxge_ldg_t *group; /* The logical device group data. */ 77 nxge_ldv_t *ldvp; 78 ddi_intr_handler_t *inthandler; 79 int vector; 80 int status1, status2; 81 82 char c = (type == VP_BOUND_TX ? 'T' : 'R'); 83 84 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_add")); 85 86 if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) { 87 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 88 "nxge_intr_add(%cDC %d): vector not found", c, channel)); 89 return (NXGE_ERROR); 90 } 91 92 ldvp = &nxge->ldgvp->ldvp[vector]; 93 group = ldvp->ldgp; 94 95 if (group->nldvs == 1) { 96 inthandler = group->ldvp->ldv_intr_handler; 97 } else if (group->nldvs > 1) { 98 inthandler = group->sys_intr_handler; 99 } else { 100 inthandler = NULL; 101 } 102 103 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 104 105 status1 = DDI_SUCCESS; 106 107 if ((status2 = ddi_intr_add_handler(interrupts->htable[vector], 108 inthandler, group->ldvp, nxge)) 109 != DDI_SUCCESS) { 110 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): " 111 "ddi_intr_add_handler(%d) returned %s", 112 c, channel, vector, nxge_ddi_perror(status2))); 113 status1 += status2; 114 } 115 116 interrupts->intr_added++; 117 118 /* Enable the interrupt. */ 119 if ((status2 = ddi_intr_enable(interrupts->htable[vector])) 120 != DDI_SUCCESS) { 121 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_add(%cDC %d): " 122 "ddi_intr_enable(%d) returned %s", 123 c, channel, vector, nxge_ddi_perror(status2))); 124 status1 += status2; 125 } 126 127 if (status1 == DDI_SUCCESS) { 128 interrupts->intr_enabled = B_TRUE; 129 130 /* Finally, arm the interrupt. */ 131 if (group->nldvs == 1) { 132 npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge); 133 (void) npi_intr_ldg_mgmt_set(handle, group->ldg, 134 B_TRUE, group->ldg_timer); 135 } 136 } 137 138 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_add")); 139 140 return (NXGE_OK); 141 } 142 143 /* 144 * nxge_intr_remove 145 * 146 * Remove <channel>'s interrupt. 147 * 148 * Arguments: 149 * nxge 150 * type Tx or Rx 151 * channel The channel whose interrupt we want to remove. 152 * 153 * Notes: 154 * Remove here means: disarm, disable, & remove the handler. 155 * 156 * Context: 157 * Service domain 158 * 159 */ 160 nxge_status_t 161 nxge_intr_remove( 162 nxge_t *nxge, 163 vpc_type_t type, 164 int channel) 165 { 166 nxge_intr_t *interrupts; /* The global interrupt data. */ 167 nxge_ldg_t *group; /* The logical device group data. */ 168 nxge_ldv_t *ldvp; 169 170 int vector; 171 int status1, status2; 172 173 char c = (type == VP_BOUND_TX ? 'T' : 'R'); 174 175 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_intr_remove")); 176 177 if ((vector = nxge_intr_vec_find(nxge, type, channel)) == -1) { 178 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 179 "nxge_intr_remove(%cDC %d): vector not found", c, channel)); 180 return (NXGE_ERROR); 181 } 182 183 ldvp = &nxge->ldgvp->ldvp[vector]; 184 group = ldvp->ldgp; 185 186 /* Disarm the interrupt. */ 187 if (group->nldvs == 1) { 188 npi_handle_t handle = NXGE_DEV_NPI_HANDLE(nxge); 189 group->arm = B_FALSE; 190 (void) npi_intr_ldg_mgmt_set(handle, group->ldg, 191 B_TRUE, group->ldg_timer); 192 group->arm = B_TRUE; /* HIOXXX There IS a better way */ 193 } 194 195 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 196 197 status1 = DDI_SUCCESS; 198 199 /* Disable the interrupt. */ 200 if ((status2 = ddi_intr_disable(interrupts->htable[vector])) 201 != DDI_SUCCESS) { 202 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)" 203 ": ddi_intr_disable(%d) returned %s", 204 c, channel, vector, nxge_ddi_perror(status2))); 205 status1 += status2; 206 } 207 208 /* Remove the interrupt handler. */ 209 if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector])) 210 != DDI_SUCCESS) { 211 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, "nxge_intr_remove(%cDC %d)" 212 ": ddi_intr_remove_handler(%d) returned %s", 213 c, channel, vector, nxge_ddi_perror(status2))); 214 status1 += status2; 215 } 216 217 if (status1 == DDI_SUCCESS) { 218 interrupts->intr_added--; 219 if (interrupts->intr_added == 0) 220 interrupts->intr_enabled = B_FALSE; 221 } 222 223 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_remove")); 224 225 return (NXGE_OK); 226 } 227 228 /* 229 * nxge_intr_vec_find 230 * 231 * Find the interrupt vector associated with <channel>. 232 * 233 * Arguments: 234 * nxge 235 * type Tx or Rx 236 * channel The channel whose vector we want to find. 237 * 238 * Notes: 239 * 240 * Context: 241 * Service domain 242 * 243 */ 244 static 245 int 246 nxge_intr_vec_find( 247 nxge_t *nxge, 248 vpc_type_t type, 249 int channel) 250 { 251 nxge_hw_pt_cfg_t *hardware; 252 nxge_ldgv_t *ldgvp; 253 nxge_ldv_t *ldvp; 254 255 int first, limit, vector; 256 257 NXGE_DEBUG_MSG((nxge, HIO_CTL, 258 "==> nxge_intr_vec_find(%cDC %d)", 259 type == VP_BOUND_TX ? 'T' : 'R', channel)); 260 261 if (nxge->ldgvp == 0) { 262 NXGE_DEBUG_MSG((nxge, HIO_CTL, 263 "nxge_hio_intr_vec_find(%cDC %d): ldgvp == 0", 264 type == VP_BOUND_TX ? 'T' : 'R', channel)); 265 return (-1); 266 } 267 268 hardware = &nxge->pt_config.hw_config; 269 270 first = hardware->ldg_chn_start; 271 if (type == VP_BOUND_TX) { 272 first += 8; /* HIOXXX N2/NIU hack */ 273 limit = first + hardware->tdc.count; 274 } else { 275 limit = first + hardware->max_rdcs; 276 } 277 278 ldgvp = nxge->ldgvp; 279 for (vector = first; vector < limit; vector++) { 280 ldvp = &ldgvp->ldvp[vector]; 281 if (ldvp->channel == channel) 282 break; 283 } 284 285 if (vector == limit) { 286 return (-1); 287 } 288 289 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_intr_vec_find")); 290 291 return (vector); 292 } 293 294 /* 295 * --------------------------------------------------------------------- 296 * HIO-specific interrupt functions. 297 * --------------------------------------------------------------------- 298 */ 299 300 /* 301 * nxge_hio_intr_add 302 * 303 * Add <channel>'s interrupt. 304 * 305 * Arguments: 306 * nxge 307 * type Tx or Rx 308 * channel The channel whose interrupt we want to remove. 309 * 310 * Notes: 311 * 312 * Context: 313 * Guest domain 314 * 315 */ 316 nxge_status_t 317 nxge_hio_intr_add( 318 nxge_t *nxge, 319 vpc_type_t type, 320 int channel) 321 { 322 nxge_hio_dc_t *dc; /* The relevant DMA channel data structure. */ 323 nxge_intr_t *interrupts; /* The global interrupt data. */ 324 nxge_ldg_t *group; /* The logical device group data. */ 325 ddi_intr_handler_t *inthandler; 326 327 int vector; /* A shorthand variable */ 328 int ddi_status; /* The response to ddi_intr_add_handler */ 329 330 char c = (type == VP_BOUND_TX ? 'T' : 'R'); 331 332 NXGE_DEBUG_MSG((nxge, HIO_CTL, 333 "==> nxge_hio_intr_add(%cDC %d)", c, channel)); 334 335 if (nxge->ldgvp == 0) { 336 NXGE_DEBUG_MSG((nxge, HIO_CTL, 337 "nxge_hio_intr_add(%cDC %d): ldgvp == 0", c, channel)); 338 return (NXGE_ERROR); 339 } 340 341 if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) { 342 NXGE_DEBUG_MSG((nxge, HIO_CTL, 343 "nxge_hio_intr_add: find(%s, %d) failed", c, channel)); 344 return (NXGE_ERROR); 345 } 346 347 /* 'nxge_intr_type' is a bad name for this data structure. */ 348 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 349 350 /* Set <vector> here to make the following code easier to read. */ 351 vector = dc->ldg.vector; 352 353 group = &nxge->ldgvp->ldgp[vector]; 354 355 if (group->nldvs == 1) { 356 inthandler = group->ldvp->ldv_intr_handler; 357 } else if (group->nldvs > 1) { 358 inthandler = group->sys_intr_handler; 359 } else { 360 inthandler = NULL; 361 } 362 363 if ((ddi_status = ddi_intr_add_handler(interrupts->htable[vector], 364 inthandler, group->ldvp, nxge)) 365 != DDI_SUCCESS) { 366 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 367 "nxge_hio_intr_add(%cDC %d): " 368 "ddi_intr_add_handler(%d) returned %s", 369 c, channel, vector, nxge_ddi_perror(ddi_status))); 370 return (NXGE_ERROR); 371 } 372 373 interrupts->intr_added++; 374 375 /* Enable the interrupt. */ 376 if ((ddi_status = ddi_intr_enable(interrupts->htable[vector])) 377 != DDI_SUCCESS) { 378 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 379 "nxge_hio_intr_add(%cDC %d): " 380 "ddi_intr_enable(%d) returned %s", 381 c, channel, vector, nxge_ddi_perror(ddi_status))); 382 return (NXGE_ERROR); 383 } 384 385 interrupts->intr_enabled = B_TRUE; 386 387 /* 388 * Note: RDC interrupts will be armed in nxge_m_start(). This 389 * prevents us from getting an interrupt before we are ready 390 * to process packets. 391 */ 392 if (type == VP_BOUND_TX) { 393 nxge_hio_ldgimgn(nxge, group); 394 } 395 396 dc->interrupting = B_TRUE; 397 398 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add")); 399 400 return (NXGE_OK); 401 } 402 403 /* 404 * nxge_hio_intr_remove 405 * 406 * Remove <channel>'s interrupt. 407 * 408 * Arguments: 409 * nxge 410 * type Tx or Rx 411 * channel The channel whose interrupt we want to remove. 412 * 413 * Notes: 414 * 415 * Context: 416 * Guest domain 417 * 418 */ 419 nxge_status_t 420 nxge_hio_intr_remove( 421 nxge_t *nxge, 422 vpc_type_t type, 423 int channel) 424 { 425 nxge_hio_dc_t *dc; /* The relevant DMA channel data structure. */ 426 nxge_intr_t *interrupts; /* The global interrupt data. */ 427 nxge_ldg_t *group; /* The logical device group data. */ 428 429 int vector; /* A shorthand variable */ 430 int status1, status2; 431 432 char c = (type == VP_BOUND_TX ? 'T' : 'R'); 433 434 NXGE_DEBUG_MSG((nxge, HIO_CTL, 435 "==> nxge_hio_intr_remove(%cDC %d)", c, channel)); 436 437 if (nxge->ldgvp == 0) { 438 NXGE_DEBUG_MSG((nxge, HIO_CTL, 439 "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel)); 440 return (NXGE_ERROR); 441 } 442 443 if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) { 444 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 445 "nxge_hio_intr_remove(%cDC %d): DC FIND failed", 446 c, channel)); 447 return (NXGE_ERROR); 448 } 449 450 if (dc->interrupting == B_FALSE) { 451 NXGE_DEBUG_MSG((nxge, HIO_CTL, 452 "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE", 453 c, channel)); 454 return (NXGE_OK); 455 } 456 457 /* 'nxge_intr_type' is a bad name for this data structure. */ 458 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 459 460 /* Set <vector> here to make the following code easier to read. */ 461 vector = dc->ldg.vector; 462 463 group = &nxge->ldgvp->ldgp[vector]; 464 465 /* Disarm the interrupt. */ 466 group->arm = B_FALSE; 467 nxge_hio_ldgimgn(nxge, group); 468 group->arm = B_TRUE; /* HIOXXX There IS a better way */ 469 470 status1 = DDI_SUCCESS; 471 472 /* Disable the interrupt. */ 473 if ((status2 = ddi_intr_disable(interrupts->htable[vector])) 474 != DDI_SUCCESS) { 475 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 476 "nxge_hio_intr_remove(%cDC %d): " 477 "ddi_intr_disable(%d) returned %s", 478 c, channel, vector, nxge_ddi_perror(status2))); 479 status1 += status2; 480 } 481 482 /* Remove the interrupt handler. */ 483 if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector])) 484 != DDI_SUCCESS) { 485 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 486 "nxge_hio_intr_remove(%cDC %d): " 487 "ddi_intr_remove_handle(%d) returned %s", 488 c, channel, vector, nxge_ddi_perror(status2))); 489 status1 += status2; 490 } 491 492 if (status1 == DDI_SUCCESS) { 493 dc->interrupting = B_FALSE; 494 495 interrupts->intr_added--; 496 if (interrupts->intr_added == 0) 497 interrupts->intr_enabled = B_FALSE; 498 } 499 500 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove")); 501 502 return (NXGE_OK); 503 } 504 505 /* 506 * nxge_hio_intr_init 507 * 508 * Initialize interrupts in a guest domain. 509 * 510 * Arguments: 511 * nxge 512 * 513 * Notes: 514 * 515 * Context: 516 * Guest domain 517 * 518 */ 519 nxge_status_t 520 nxge_hio_intr_init( 521 nxge_t *nxge) 522 { 523 int *prop_val; 524 uint_t prop_len; 525 526 nxge_intr_t *interrupts; 527 528 int intr_type, behavior; 529 int nintrs, navail, nactual; 530 int inum = 0; 531 int ddi_status = DDI_SUCCESS; 532 533 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 534 int i; 535 536 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init")); 537 538 /* Look up the "interrupts" property. */ 539 if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0, 540 "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) { 541 NXGE_ERROR_MSG((nxge, HIO_CTL, 542 "==> nxge_hio_intr_init(obp): no 'interrupts' property")); 543 return (NXGE_ERROR); 544 } 545 546 /* 547 * For each device assigned, the content of each interrupts 548 * property is its logical device group. 549 * 550 * Assignment of interrupts property is in the the following 551 * order: 552 * 553 * two receive channels 554 * two transmit channels 555 */ 556 for (i = 0; i < prop_len; i++) { 557 hardware->ldg[i] = prop_val[i]; 558 NXGE_DEBUG_MSG((nxge, HIO_CTL, 559 "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d", 560 nxge->function_num, i, hardware->ldg[i])); 561 } 562 ddi_prop_free(prop_val); 563 564 hardware->max_grpids = prop_len; 565 hardware->max_ldgs = prop_len; 566 hardware->ldg_chn_start = 0; 567 568 /* ----------------------------------------------------- */ 569 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 570 571 interrupts->intr_registered = B_FALSE; 572 interrupts->intr_enabled = B_FALSE; 573 interrupts->start_inum = 0; 574 575 ddi_status = ddi_intr_get_supported_types( 576 nxge->dip, &interrupts->intr_types); 577 if (ddi_status != DDI_SUCCESS) { 578 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 579 "ddi_intr_get_supported_types() returned 0x%x, " 580 "types = 0x%x", ddi_status, interrupts->intr_types)); 581 return (NXGE_ERROR); 582 } 583 584 NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() " 585 "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types)); 586 587 /* HIOXXX hack */ 588 interrupts->intr_type = DDI_INTR_TYPE_FIXED; 589 /* HIOXXX hack */ 590 591 intr_type = interrupts->intr_type; 592 593 ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail); 594 if (ddi_status != DDI_SUCCESS) { 595 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 596 "ddi_intr_get_navail() returned %s, navail: %d", 597 ddi_status == DDI_FAILURE ? "DDI_FAILURE" : 598 "DDI_INTR_NOTFOUND", navail)); 599 return (NXGE_ERROR); 600 } 601 602 NXGE_DEBUG_MSG((nxge, HIO_CTL, 603 "nxge_hio_intr_init: number of available interrupts: %d", navail)); 604 605 ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs); 606 if (ddi_status != DDI_SUCCESS) { 607 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 608 "ddi_intr_get_nintrs() returned %s, nintrs: %d", 609 ddi_status == DDI_FAILURE ? "DDI_FAILURE" : 610 "DDI_INTR_NOTFOUND", nintrs)); 611 return (NXGE_ERROR); 612 } 613 614 NXGE_DEBUG_MSG((nxge, HIO_CTL, 615 "nxge_hio_intr_init: number of interrupts: %d", nintrs)); 616 617 interrupts->intr_size = navail * sizeof (ddi_intr_handle_t); 618 interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP); 619 620 /* 621 * When <behavior> is set to DDI_INTR_ALLOC_STRICT, 622 * ddi_intr_alloc() succeeds if and only if <navail> 623 * interrupts are are allocated. Otherwise, it fails. 624 */ 625 behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ? 626 DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL); 627 628 ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type, 629 inum, navail, &nactual, behavior); 630 if (ddi_status != DDI_SUCCESS) { 631 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 632 "ddi_intr_alloc() returned 0x%x%, " 633 "number allocated: %d", ddi_status, nactual)); 634 return (NXGE_ERROR); 635 } 636 637 NXGE_DEBUG_MSG((nxge, HIO_CTL, 638 "nxge_hio_intr_init: number of interrupts allocated: %d", nactual)); 639 640 /* <ninterrupts> is a dead variable: we may as well use it. */ 641 hardware->ninterrupts = nactual; 642 643 /* FOI: Get the interrupt priority. */ 644 if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0], 645 (uint_t *)&interrupts->pri)) != DDI_SUCCESS) { 646 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 647 " ddi_intr_get_pri() failed: %d", ddi_status)); 648 } 649 650 NXGE_DEBUG_MSG((nxge, HIO_CTL, 651 "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri)); 652 653 /* FOI: Get our interrupt capability flags. */ 654 if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0], 655 &interrupts->intr_cap)) != DDI_SUCCESS) { 656 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 657 "ddi_intr_get_cap() failed: %d", ddi_status)); 658 } 659 660 NXGE_DEBUG_MSG((nxge, HIO_CTL, 661 "nxge_hio_intr_init: interrupt capabilities: %d", 662 interrupts->intr_cap)); 663 664 interrupts->intr_registered = B_TRUE; 665 666 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init")); 667 668 return (NXGE_OK); 669 } 670 671 /* 672 * nxge_hio_intr_uninit 673 * 674 * Uninitialize interrupts in a guest domain. 675 * 676 * Arguments: 677 * nxge 678 * 679 * Notes: 680 * 681 * Context: 682 * Guest domain 683 */ 684 void 685 nxge_hio_intr_uninit( 686 nxge_t *nxge) 687 { 688 nxge_hw_pt_cfg_t *hardware; 689 nxge_intr_t *interrupts; 690 nxge_ldgv_t *control; 691 int i; 692 693 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit")); 694 695 /* ----------------------------------------------------- */ 696 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 697 698 /* 699 * If necessary, disable any currently active interrupts. 700 */ 701 if (interrupts->intr_enabled) { 702 nxge_grp_set_t *set; 703 nxge_grp_t *group; 704 int channel; 705 706 set = &nxge->tx_set; 707 group = set->group[0]; /* Assumption: only one group! */ 708 for (channel = 0; channel < NXGE_MAX_TDCS; channel++) { 709 if ((1 << channel) & group->map) { 710 (void) nxge_hio_intr_remove( 711 nxge, VP_BOUND_TX, channel); 712 } 713 } 714 715 set = &nxge->rx_set; 716 group = set->group[0]; /* Assumption: only one group! */ 717 for (channel = 0; channel < NXGE_MAX_RDCS; channel++) { 718 if ((1 << channel) & group->map) { 719 (void) nxge_hio_intr_remove( 720 nxge, VP_BOUND_RX, channel); 721 } 722 } 723 } 724 725 /* 726 * Free all of our allocated interrupts. 727 */ 728 hardware = &nxge->pt_config.hw_config; 729 for (i = 0; i < hardware->ninterrupts; i++) { 730 if (interrupts->htable[i]) 731 (void) ddi_intr_free(interrupts->htable[i]); 732 interrupts->htable[i] = 0; 733 } 734 735 interrupts->intr_registered = B_FALSE; 736 KMEM_FREE(interrupts->htable, interrupts->intr_size); 737 interrupts->htable = NULL; 738 739 if (nxge->ldgvp == NULL) 740 goto nxge_hio_intr_uninit_exit; 741 742 control = nxge->ldgvp; 743 if (control->ldgp) { 744 KMEM_FREE(control->ldgp, 745 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS); 746 control->ldgp = 0; 747 } 748 749 if (control->ldvp) { 750 KMEM_FREE(control->ldvp, 751 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS); 752 control->ldvp = 0; 753 } 754 755 KMEM_FREE(control, sizeof (nxge_ldgv_t)); 756 nxge->ldgvp = NULL; 757 758 nxge_hio_intr_uninit_exit: 759 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit")); 760 } 761 762 /* 763 * nxge_hio_tdsv_add 764 * 765 * Add a transmit device interrupt. 766 * 767 * Arguments: 768 * nxge 769 * dc The TDC whose interrupt we're adding 770 * 771 * Notes: 772 * 773 * Context: 774 * Guest domain 775 */ 776 static 777 hv_rv_t 778 nxge_hio_tdsv_add( 779 nxge_t *nxge, 780 nxge_hio_dc_t *dc) 781 { 782 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio; 783 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 784 nxhv_dc_fp_t *tx = &nhd->hio.tx; 785 hv_rv_t hv_rv; 786 787 if (tx->getinfo == 0) { 788 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 789 "nx_hio_tdsv_add: tx->getinfo absent")); 790 return (EINVAL); 791 } 792 793 /* 794 * Get the dma channel information. 795 */ 796 hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index, 797 &dc->ldg.ldsv); 798 if (hv_rv != 0) { 799 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 800 "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv)); 801 return (EIO); 802 } 803 804 NXGE_DEBUG_MSG((nxge, HIO_CTL, 805 "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d", 806 (int)dc->ldg.index, (int)dc->ldg.ldsv)); 807 808 if (hardware->tdc.count == 0) { 809 hardware->tdc.start = dc->channel; 810 } 811 812 hardware->tdc.count++; 813 hardware->tdc.owned++; 814 815 /* 816 * In version 1.0 of the hybrid I/O driver, there 817 * are eight interrupt vectors per VR. 818 * 819 * Vectors 0 - 3 are reserved for RDCs. 820 * Vectors 4 - 7 are reserved for TDCs. 821 */ 822 dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE; 823 // Version 1.0 hack only! 824 825 return (0); 826 } 827 828 /* 829 * nxge_hio_rdsv_add 830 * 831 * Add a transmit device interrupt. 832 * 833 * Arguments: 834 * nxge 835 * dc The RDC whose interrupt we're adding 836 * 837 * Notes: 838 * 839 * Context: 840 * Guest domain 841 */ 842 static 843 hv_rv_t 844 nxge_hio_rdsv_add( 845 nxge_t *nxge, 846 nxge_hio_dc_t *dc) 847 { 848 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio; 849 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 850 nxhv_dc_fp_t *rx = &nhd->hio.rx; 851 hv_rv_t hv_rv; 852 853 if (rx->getinfo == 0) { 854 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 855 "nx_hio_tdsv_add: rx->getinfo absent")); 856 return (EINVAL); 857 } 858 859 /* 860 * Get DMA channel information. 861 */ 862 hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index, 863 &dc->ldg.ldsv); 864 if (hv_rv != 0) { 865 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 866 "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv)); 867 return (EIO); 868 } 869 870 NXGE_DEBUG_MSG((nxge, HIO_CTL, 871 "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d", 872 (int)dc->ldg.index, (int)dc->ldg.ldsv)); 873 874 if (hardware->max_rdcs == 0) { 875 hardware->start_rdc = dc->channel; 876 hardware->def_rdc = dc->channel; 877 } 878 879 hardware->max_rdcs++; 880 881 /* 882 * In version 1.0 of the hybrid I/O driver, there 883 * are eight interrupt vectors per VR. 884 * 885 * Vectors 0 - 3 are reserved for RDCs. 886 */ 887 dc->ldg.vector = (dc->ldg.ldsv % 2); 888 // Version 1.0 hack only! 889 890 return (0); 891 } 892 893 /* 894 * nxge_hio_ldsv_add 895 * 896 * Add a transmit or receive interrupt. 897 * 898 * Arguments: 899 * nxge 900 * dc The DMA channel whose interrupt we're adding 901 * 902 * Notes: 903 * Guest domains can only add interrupts for DMA channels. 904 * They cannot access the MAC, MIF, or SYSERR interrupts. 905 * 906 * Context: 907 * Guest domain 908 */ 909 int 910 nxge_hio_ldsv_add(nxge_t *nxge, nxge_hio_dc_t *dc) 911 { 912 nxge_ldgv_t *control; 913 nxge_ldg_t *group; 914 nxge_ldv_t *device; 915 916 if (dc->type == VP_BOUND_TX) { 917 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)", 918 dc->channel)); 919 if (nxge_hio_tdsv_add(nxge, dc) != 0) 920 return (EIO); 921 } else { 922 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)", 923 dc->channel)); 924 if (nxge_hio_rdsv_add(nxge, dc) != 0) 925 return (EIO); 926 } 927 928 dc->ldg.map |= (1 << dc->ldg.ldsv); 929 930 control = nxge->ldgvp; 931 if (control == NULL) { 932 control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP); 933 nxge->ldgvp = control; 934 control->maxldgs = 1; 935 control->maxldvs = 1; 936 control->ldgp = KMEM_ZALLOC( 937 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP); 938 control->ldvp = KMEM_ZALLOC( 939 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP); 940 } else { 941 control->maxldgs++; 942 control->maxldvs++; 943 } 944 945 /* 946 * Initialize the logical device group data structure first. 947 */ 948 group = &control->ldgp[dc->ldg.vector]; 949 950 (void) memset(group, 0, sizeof (*group)); 951 952 /* 953 * <hw_config.ldg> is a copy of the "interrupts" property. 954 */ 955 group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector]; 956 group->vldg_index = (uint8_t)dc->ldg.index; 957 /* 958 * Since <vldg_index> is a dead variable, I'm reusing 959 * it in Hybrid I/O to calculate the offset into the 960 * virtual PIO_LDSV space. 961 */ 962 963 group->arm = B_TRUE; 964 group->ldg_timer = NXGE_TIMER_LDG; 965 group->func = nxge->function_num; 966 group->vector = dc->ldg.vector; 967 /* 968 * <intdata> appears to be a dead variable. 969 * Though it is not used anywhere in the driver, 970 * we'll set it anyway. 971 */ 972 group->intdata = SID_DATA(group->func, group->vector); 973 974 group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */ 975 group->nxgep = nxge; 976 977 /* 978 * Initialize the logical device state vector next. 979 */ 980 device = &control->ldvp[dc->ldg.ldsv]; 981 982 device->ldg_assigned = group->ldg; 983 device->ldv = dc->ldg.ldsv; 984 985 if (dc->type == VP_BOUND_TX) { 986 device->is_txdma = B_TRUE; 987 device->is_rxdma = B_FALSE; 988 device->ldv_intr_handler = nxge_tx_intr; 989 } else { 990 device->is_rxdma = B_TRUE; 991 device->is_txdma = B_FALSE; 992 device->ldv_intr_handler = nxge_rx_intr; 993 } 994 device->is_mif = B_FALSE; 995 device->is_mac = B_FALSE; 996 device->is_syserr = B_FALSE; 997 device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */ 998 999 device->channel = dc->channel; 1000 device->vdma_index = dc->page; 1001 device->func = nxge->function_num; 1002 device->ldgp = group; 1003 device->ldv_flags = 0; 1004 device->ldv_ldf_masks = 0; 1005 1006 device->nxgep = nxge; 1007 1008 /* 1009 * This code seems to imply a strict 1-to-1 correspondence. 1010 */ 1011 group->nldvs++; 1012 group->ldvp = device; 1013 1014 control->nldvs++; 1015 control->ldg_intrs++; 1016 1017 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add")); 1018 1019 return (0); 1020 } 1021 1022 /* 1023 * nxge_hio_ldsv_im 1024 * 1025 * Manage a VLDG's interrupts. 1026 * 1027 * Arguments: 1028 * nxge 1029 * group The VLDG to manage 1030 * 1031 * Notes: 1032 * There are 8 sets of 4 64-bit registers per VR, 1 per LDG. 1033 * That sums to 256 bytes of virtual PIO_LDSV space. 1034 * 1035 * VLDG0 starts at offset 0, 1036 * VLDG1 starts at offset 32, etc. 1037 * 1038 * Each set consists of 4 registers: 1039 * Logical Device State Vector 0. LDSV0 1040 * Logical Device State Vector 1. LDSV1 1041 * Logical Device State Vector 2. LDSV2 1042 * Logical Device Group Interrupt Management. LDGIMGN 1043 * 1044 * The first three (LDSVx) are read-only. The 4th register is the 1045 * LDGIMGN, the LDG Interrupt Management register, which is used to 1046 * arm the LDG, or set its timer. 1047 * 1048 * The offset to write to is calculated as follows: 1049 * 1050 * 0x2000 + (VLDG << 4) + offset, where: 1051 * VDLG is the virtual group, i.e., index of the LDG. 1052 * offset is the offset (alignment 8) of the register 1053 * to read or write. 1054 * 1055 * So, for example, if we wanted to arm the first TDC of VRx, we would 1056 * calculate the address as: 1057 * 1058 * 0x2000 + (0 << 4) + 0x18 = 0x18 1059 * 1060 * Context: 1061 * Guest domain 1062 * 1063 */ 1064 void 1065 nxge_hio_ldsv_im( 1066 /* Read any register in the PIO_LDSV space. */ 1067 nxge_t *nxge, 1068 nxge_ldg_t *group, 1069 pio_ld_op_t op, 1070 uint64_t *value) 1071 { 1072 uint64_t offset = VLDG_OFFSET; 1073 1074 offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */ 1075 offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */ 1076 1077 NXGE_REG_RD64(nxge->npi_handle, offset, value); 1078 } 1079 1080 void 1081 nxge_hio_ldgimgn( 1082 /* Write the PIO_LDGIMGN register. */ 1083 nxge_t *nxge, 1084 nxge_ldg_t *group) 1085 { 1086 uint64_t offset = VLDG_OFFSET; 1087 ldgimgm_t mgm; 1088 1089 offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */ 1090 offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */ 1091 1092 mgm.value = 0; 1093 if (group->arm) { 1094 mgm.bits.ldw.arm = 1; 1095 mgm.bits.ldw.timer = group->ldg_timer; 1096 } else { 1097 mgm.bits.ldw.arm = 0; 1098 mgm.bits.ldw.timer = 0; 1099 } 1100 NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value); 1101 } 1102