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