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