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 /* 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 /* Finally, arm the interrupt. */ 385 nxge_hio_ldgimgn(nxge, group); 386 387 dc->interrupting = B_TRUE; 388 389 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_add")); 390 391 return (NXGE_OK); 392 } 393 394 /* 395 * nxge_hio_intr_remove 396 * 397 * Remove <channel>'s interrupt. 398 * 399 * Arguments: 400 * nxge 401 * type Tx or Rx 402 * channel The channel whose interrupt we want to remove. 403 * 404 * Notes: 405 * 406 * Context: 407 * Guest domain 408 * 409 */ 410 nxge_status_t 411 nxge_hio_intr_remove( 412 nxge_t *nxge, 413 vpc_type_t type, 414 int channel) 415 { 416 nxge_hio_dc_t *dc; /* The relevant DMA channel data structure. */ 417 nxge_intr_t *interrupts; /* The global interrupt data. */ 418 nxge_ldg_t *group; /* The logical device group data. */ 419 420 int vector; /* A shorthand variable */ 421 int status1, status2; 422 423 char c = (type == VP_BOUND_TX ? 'T' : 'R'); 424 425 NXGE_DEBUG_MSG((nxge, HIO_CTL, 426 "==> nxge_hio_intr_remove(%cDC %d)", c, channel)); 427 428 if (nxge->ldgvp == 0) { 429 NXGE_DEBUG_MSG((nxge, HIO_CTL, 430 "nxge_hio_intr_remove(%cDC %d): ldgvp == 0", c, channel)); 431 return (NXGE_ERROR); 432 } 433 434 if ((dc = nxge_grp_dc_find(nxge, type, channel)) == 0) { 435 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 436 "nxge_hio_intr_remove(%cDC %d): DC FIND failed", 437 c, channel)); 438 return (NXGE_ERROR); 439 } 440 441 if (dc->interrupting == B_FALSE) { 442 NXGE_DEBUG_MSG((nxge, HIO_CTL, 443 "nxge_hio_intr_remove(%cDC %d): interrupting == FALSE", 444 c, channel)); 445 return (NXGE_OK); 446 } 447 448 /* 'nxge_intr_type' is a bad name for this data structure. */ 449 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 450 451 /* Set <vector> here to make the following code easier to read. */ 452 vector = dc->ldg.vector; 453 454 group = &nxge->ldgvp->ldgp[vector]; 455 456 /* Disarm the interrupt. */ 457 group->arm = B_FALSE; 458 nxge_hio_ldgimgn(nxge, group); 459 group->arm = B_TRUE; /* HIOXXX There IS a better way */ 460 461 status1 = DDI_SUCCESS; 462 463 /* Disable the interrupt. */ 464 if ((status2 = ddi_intr_disable(interrupts->htable[vector])) 465 != DDI_SUCCESS) { 466 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 467 "nxge_hio_intr_remove(%cDC %d): " 468 "ddi_intr_disable(%d) returned %s", 469 c, channel, vector, nxge_ddi_perror(status2))); 470 status1 += status2; 471 } 472 473 /* Remove the interrupt handler. */ 474 if ((status2 = ddi_intr_remove_handler(interrupts->htable[vector])) 475 != DDI_SUCCESS) { 476 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 477 "nxge_hio_intr_remove(%cDC %d): " 478 "ddi_intr_remove_handle(%d) returned %s", 479 c, channel, vector, nxge_ddi_perror(status2))); 480 status1 += status2; 481 } 482 483 if (status1 == DDI_SUCCESS) { 484 dc->interrupting = B_FALSE; 485 486 interrupts->intr_added--; 487 if (interrupts->intr_added == 0) 488 interrupts->intr_enabled = B_FALSE; 489 } 490 491 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_remove")); 492 493 return (NXGE_OK); 494 } 495 496 /* 497 * nxge_hio_intr_init 498 * 499 * Initialize interrupts in a guest domain. 500 * 501 * Arguments: 502 * nxge 503 * 504 * Notes: 505 * 506 * Context: 507 * Guest domain 508 * 509 */ 510 nxge_status_t 511 nxge_hio_intr_init( 512 nxge_t *nxge) 513 { 514 int *prop_val; 515 uint_t prop_len; 516 517 nxge_intr_t *interrupts; 518 519 int intr_type, behavior; 520 int nintrs, navail, nactual; 521 int inum = 0; 522 int ddi_status = DDI_SUCCESS; 523 524 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 525 int i; 526 527 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_init")); 528 529 /* Look up the "interrupts" property. */ 530 if ((ddi_prop_lookup_int_array(DDI_DEV_T_ANY, nxge->dip, 0, 531 "interrupts", &prop_val, &prop_len)) != DDI_PROP_SUCCESS) { 532 NXGE_ERROR_MSG((nxge, HIO_CTL, 533 "==> nxge_hio_intr_init(obp): no 'interrupts' property")); 534 return (NXGE_ERROR); 535 } 536 537 /* 538 * For each device assigned, the content of each interrupts 539 * property is its logical device group. 540 * 541 * Assignment of interrupts property is in the the following 542 * order: 543 * 544 * two receive channels 545 * two transmit channels 546 */ 547 for (i = 0; i < prop_len; i++) { 548 hardware->ldg[i] = prop_val[i]; 549 NXGE_DEBUG_MSG((nxge, HIO_CTL, 550 "==> nxge_hio_intr_init(obp): F%d: interrupt #%d, ldg %d", 551 nxge->function_num, i, hardware->ldg[i])); 552 } 553 ddi_prop_free(prop_val); 554 555 hardware->max_grpids = prop_len; 556 hardware->max_ldgs = prop_len; 557 hardware->ldg_chn_start = 0; 558 559 /* ----------------------------------------------------- */ 560 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 561 562 interrupts->intr_registered = B_FALSE; 563 interrupts->intr_enabled = B_FALSE; 564 interrupts->start_inum = 0; 565 566 ddi_status = ddi_intr_get_supported_types( 567 nxge->dip, &interrupts->intr_types); 568 if (ddi_status != DDI_SUCCESS) { 569 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 570 "ddi_intr_get_supported_types() returned 0x%x, " 571 "types = 0x%x", ddi_status, interrupts->intr_types)); 572 return (NXGE_ERROR); 573 } 574 575 NXGE_ERROR_MSG((nxge, HIO_CTL, "ddi_intr_get_supported_types() " 576 "returned 0x%x, types = 0x%x", ddi_status, interrupts->intr_types)); 577 578 /* HIOXXX hack */ 579 interrupts->intr_type = DDI_INTR_TYPE_FIXED; 580 /* HIOXXX hack */ 581 582 intr_type = interrupts->intr_type; 583 584 ddi_status = ddi_intr_get_navail(nxge->dip, intr_type, &navail); 585 if (ddi_status != DDI_SUCCESS) { 586 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 587 "ddi_intr_get_navail() returned %s, navail: %d", 588 ddi_status == DDI_FAILURE ? "DDI_FAILURE" : 589 "DDI_INTR_NOTFOUND", navail)); 590 return (NXGE_ERROR); 591 } 592 593 NXGE_DEBUG_MSG((nxge, HIO_CTL, 594 "nxge_hio_intr_init: number of available interrupts: %d", navail)); 595 596 ddi_status = ddi_intr_get_nintrs(nxge->dip, intr_type, &nintrs); 597 if (ddi_status != DDI_SUCCESS) { 598 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 599 "ddi_intr_get_nintrs() returned %s, nintrs: %d", 600 ddi_status == DDI_FAILURE ? "DDI_FAILURE" : 601 "DDI_INTR_NOTFOUND", nintrs)); 602 return (NXGE_ERROR); 603 } 604 605 NXGE_DEBUG_MSG((nxge, HIO_CTL, 606 "nxge_hio_intr_init: number of interrupts: %d", nintrs)); 607 608 interrupts->intr_size = navail * sizeof (ddi_intr_handle_t); 609 interrupts->htable = kmem_alloc(interrupts->intr_size, KM_SLEEP); 610 611 /* 612 * When <behavior> is set to DDI_INTR_ALLOC_STRICT, 613 * ddi_intr_alloc() succeeds if and only if <navail> 614 * interrupts are are allocated. Otherwise, it fails. 615 */ 616 behavior = ((intr_type == DDI_INTR_TYPE_FIXED) ? 617 DDI_INTR_ALLOC_STRICT : DDI_INTR_ALLOC_NORMAL); 618 619 ddi_status = ddi_intr_alloc(nxge->dip, interrupts->htable, intr_type, 620 inum, navail, &nactual, behavior); 621 if (ddi_status != DDI_SUCCESS) { 622 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 623 "ddi_intr_alloc() returned 0x%x%, " 624 "number allocated: %d", ddi_status, nactual)); 625 return (NXGE_ERROR); 626 } 627 628 NXGE_DEBUG_MSG((nxge, HIO_CTL, 629 "nxge_hio_intr_init: number of interrupts allocated: %d", nactual)); 630 631 /* <ninterrupts> is a dead variable: we may as well use it. */ 632 hardware->ninterrupts = nactual; 633 634 /* FOI: Get the interrupt priority. */ 635 if ((ddi_status = ddi_intr_get_pri(interrupts->htable[0], 636 (uint_t *)&interrupts->pri)) != DDI_SUCCESS) { 637 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 638 " ddi_intr_get_pri() failed: %d", ddi_status)); 639 } 640 641 NXGE_DEBUG_MSG((nxge, HIO_CTL, 642 "nxge_hio_intr_init: interrupt priority: %d", interrupts->pri)); 643 644 /* FOI: Get our interrupt capability flags. */ 645 if ((ddi_status = ddi_intr_get_cap(interrupts->htable[0], 646 &interrupts->intr_cap)) != DDI_SUCCESS) { 647 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 648 "ddi_intr_get_cap() failed: %d", ddi_status)); 649 } 650 651 NXGE_DEBUG_MSG((nxge, HIO_CTL, 652 "nxge_hio_intr_init: interrupt capabilities: %d", 653 interrupts->intr_cap)); 654 655 interrupts->intr_registered = B_TRUE; 656 657 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_init")); 658 659 return (NXGE_OK); 660 } 661 662 /* 663 * nxge_hio_intr_uninit 664 * 665 * Uninitialize interrupts in a guest domain. 666 * 667 * Arguments: 668 * nxge 669 * 670 * Notes: 671 * 672 * Context: 673 * Guest domain 674 */ 675 void 676 nxge_hio_intr_uninit( 677 nxge_t *nxge) 678 { 679 nxge_hw_pt_cfg_t *hardware; 680 nxge_intr_t *interrupts; 681 nxge_ldgv_t *control; 682 int i; 683 684 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_intr_uninit")); 685 686 /* ----------------------------------------------------- */ 687 interrupts = (nxge_intr_t *)&nxge->nxge_intr_type; 688 689 /* 690 * If necessary, disable any currently active interrupts. 691 */ 692 if (interrupts->intr_enabled) { 693 nxge_grp_set_t *set; 694 nxge_grp_t *group; 695 int channel; 696 697 set = &nxge->tx_set; 698 group = set->group[0]; /* Assumption: only one group! */ 699 for (channel = 0; channel < NXGE_MAX_TDCS; channel++) { 700 if ((1 << channel) & group->map) { 701 (void) nxge_hio_intr_remove( 702 nxge, VP_BOUND_TX, channel); 703 } 704 } 705 706 set = &nxge->rx_set; 707 group = set->group[0]; /* Assumption: only one group! */ 708 for (channel = 0; channel < NXGE_MAX_RDCS; channel++) { 709 if ((1 << channel) & group->map) { 710 (void) nxge_hio_intr_remove( 711 nxge, VP_BOUND_RX, channel); 712 } 713 } 714 } 715 716 /* 717 * Free all of our allocated interrupts. 718 */ 719 hardware = &nxge->pt_config.hw_config; 720 for (i = 0; i < hardware->ninterrupts; i++) { 721 if (interrupts->htable[i]) 722 (void) ddi_intr_free(interrupts->htable[i]); 723 interrupts->htable[i] = 0; 724 } 725 726 interrupts->intr_registered = B_FALSE; 727 728 if (nxge->ldgvp == NULL) 729 goto nxge_hio_intr_uninit_exit; 730 731 control = nxge->ldgvp; 732 if (control->ldgp) { 733 KMEM_FREE(control->ldgp, 734 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS); 735 control->ldgp = 0; 736 } 737 738 if (control->ldvp) { 739 KMEM_FREE(control->ldvp, 740 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS); 741 control->ldvp = 0; 742 } 743 744 nxge_hio_intr_uninit_exit: 745 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit")); 746 } 747 748 /* 749 * nxge_hio_tdsv_add 750 * 751 * Add a transmit device interrupt. 752 * 753 * Arguments: 754 * nxge 755 * dc The TDC whose interrupt we're adding 756 * 757 * Notes: 758 * 759 * Context: 760 * Guest domain 761 */ 762 static 763 hv_rv_t 764 nxge_hio_tdsv_add( 765 nxge_t *nxge, 766 nxge_hio_dc_t *dc) 767 { 768 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio; 769 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 770 nxhv_dc_fp_t *tx = &nhd->hio.tx; 771 hv_rv_t hv_rv; 772 773 if (tx->getinfo == 0) { 774 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 775 "nx_hio_tdsv_add: tx->getinfo absent")); 776 return (EINVAL); 777 } 778 779 /* 780 * Get the dma channel information. 781 */ 782 hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index, 783 &dc->ldg.ldsv); 784 if (hv_rv != 0) { 785 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 786 "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv)); 787 return (EIO); 788 } 789 790 NXGE_DEBUG_MSG((nxge, HIO_CTL, 791 "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d", 792 (int)dc->ldg.index, (int)dc->ldg.ldsv)); 793 794 if (hardware->tdc.count == 0) { 795 hardware->tdc.start = dc->channel; 796 } 797 798 hardware->tdc.count++; 799 hardware->tdc.owned++; 800 801 /* 802 * In version 1.0 of the hybrid I/O driver, there 803 * are eight interrupt vectors per VR. 804 * 805 * Vectors 0 - 3 are reserved for RDCs. 806 * Vectors 4 - 7 are reserved for TDCs. 807 */ 808 dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE; 809 // Version 1.0 hack only! 810 811 return (0); 812 } 813 814 /* 815 * nxge_hio_rdsv_add 816 * 817 * Add a transmit device interrupt. 818 * 819 * Arguments: 820 * nxge 821 * dc The RDC whose interrupt we're adding 822 * 823 * Notes: 824 * 825 * Context: 826 * Guest domain 827 */ 828 static 829 hv_rv_t 830 nxge_hio_rdsv_add( 831 nxge_t *nxge, 832 nxge_hio_dc_t *dc) 833 { 834 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio; 835 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 836 nxhv_dc_fp_t *rx = &nhd->hio.rx; 837 hv_rv_t hv_rv; 838 839 if (rx->getinfo == 0) { 840 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 841 "nx_hio_tdsv_add: rx->getinfo absent")); 842 return (EINVAL); 843 } 844 845 /* 846 * Get DMA channel information. 847 */ 848 hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index, 849 &dc->ldg.ldsv); 850 if (hv_rv != 0) { 851 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 852 "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv)); 853 return (EIO); 854 } 855 856 NXGE_DEBUG_MSG((nxge, HIO_CTL, 857 "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d", 858 (int)dc->ldg.index, (int)dc->ldg.ldsv)); 859 860 if (hardware->max_rdcs == 0) { 861 hardware->start_rdc = dc->channel; 862 hardware->def_rdc = dc->channel; 863 } 864 865 hardware->max_rdcs++; 866 867 /* 868 * In version 1.0 of the hybrid I/O driver, there 869 * are eight interrupt vectors per VR. 870 * 871 * Vectors 0 - 3 are reserved for RDCs. 872 */ 873 dc->ldg.vector = (dc->ldg.ldsv % 2); 874 // Version 1.0 hack only! 875 876 return (0); 877 } 878 879 /* 880 * nxge_hio_ldsv_add 881 * 882 * Add a transmit or receive interrupt. 883 * 884 * Arguments: 885 * nxge 886 * dc The DMA channel whose interrupt we're adding 887 * 888 * Notes: 889 * Guest domains can only add interrupts for DMA channels. 890 * They cannot access the MAC, MIF, or SYSERR interrupts. 891 * 892 * Context: 893 * Guest domain 894 */ 895 hv_rv_t 896 nxge_hio_ldsv_add( 897 nxge_t *nxge, 898 nxge_hio_dc_t *dc) 899 { 900 nxge_ldgv_t *control; 901 nxge_ldg_t *group; 902 nxge_ldv_t *device; 903 hv_rv_t hv_rv; 904 905 if (dc->type == VP_BOUND_TX) { 906 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(TDC %d)", 907 dc->channel)); 908 if ((hv_rv = nxge_hio_tdsv_add(nxge, dc)) != 0) 909 return (hv_rv); 910 } else { 911 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)", 912 dc->channel)); 913 if ((hv_rv = nxge_hio_rdsv_add(nxge, dc)) != 0) 914 return (hv_rv); 915 } 916 917 dc->ldg.map |= (1 << dc->ldg.ldsv); 918 919 control = nxge->ldgvp; 920 if (control == NULL) { 921 control = KMEM_ZALLOC(sizeof (nxge_ldgv_t), KM_SLEEP); 922 nxge->ldgvp = control; 923 control->maxldgs = 1; 924 control->maxldvs = 1; 925 control->ldgp = KMEM_ZALLOC( 926 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS, KM_SLEEP); 927 control->ldvp = KMEM_ZALLOC( 928 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS, KM_SLEEP); 929 } else { 930 control->maxldgs++; 931 control->maxldvs++; 932 } 933 934 /* 935 * Initialize the logical device group data structure first. 936 */ 937 group = &control->ldgp[dc->ldg.vector]; 938 939 (void) memset(group, 0, sizeof (*group)); 940 941 /* 942 * <hw_config.ldg> is a copy of the "interrupts" property. 943 */ 944 group->ldg = nxge->pt_config.hw_config.ldg[dc->ldg.vector]; 945 group->vldg_index = (uint8_t)dc->ldg.index; 946 /* 947 * Since <vldg_index> is a dead variable, I'm reusing 948 * it in Hybrid I/O to calculate the offset into the 949 * virtual PIO_LDSV space. 950 */ 951 952 group->arm = B_TRUE; 953 group->ldg_timer = NXGE_TIMER_LDG; 954 group->func = nxge->function_num; 955 group->vector = dc->ldg.vector; 956 /* 957 * <intdata> appears to be a dead variable. 958 * Though it is not used anywhere in the driver, 959 * we'll set it anyway. 960 */ 961 group->intdata = SID_DATA(group->func, group->vector); 962 963 group->sys_intr_handler = nxge_intr; /* HIOXXX Does this work? */ 964 group->nxgep = nxge; 965 966 /* 967 * Initialize the logical device state vector next. 968 */ 969 device = &control->ldvp[dc->ldg.ldsv]; 970 971 device->ldg_assigned = group->ldg; 972 device->ldv = dc->ldg.ldsv; 973 974 if (dc->type == VP_BOUND_TX) { 975 device->is_txdma = B_TRUE; 976 device->is_rxdma = B_FALSE; 977 device->ldv_intr_handler = nxge_tx_intr; 978 } else { 979 device->is_rxdma = B_TRUE; 980 device->is_txdma = B_FALSE; 981 device->ldv_intr_handler = nxge_rx_intr; 982 } 983 device->is_mif = B_FALSE; 984 device->is_mac = B_FALSE; 985 device->is_syserr = B_FALSE; 986 device->use_timer = B_FALSE; /* Set to B_TRUE for syserr only. */ 987 988 device->channel = dc->channel; 989 device->vdma_index = dc->page; 990 device->func = nxge->function_num; 991 device->ldgp = group; 992 device->ldv_flags = 0; 993 device->ldv_ldf_masks = 0; 994 995 device->nxgep = nxge; 996 997 /* 998 * This code seems to imply a strict 1-to-1 correspondence. 999 */ 1000 group->nldvs++; 1001 group->ldvp = device; 1002 1003 control->nldvs++; 1004 control->ldg_intrs++; 1005 1006 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_ldsv_add")); 1007 1008 return (0); 1009 } 1010 1011 /* 1012 * nxge_hio_ldsv_im 1013 * 1014 * Manage a VLDG's interrupts. 1015 * 1016 * Arguments: 1017 * nxge 1018 * group The VLDG to manage 1019 * 1020 * Notes: 1021 * There are 8 sets of 4 64-bit registers per VR, 1 per LDG. 1022 * That sums to 256 bytes of virtual PIO_LDSV space. 1023 * 1024 * VLDG0 starts at offset 0, 1025 * VLDG1 starts at offset 32, etc. 1026 * 1027 * Each set consists of 4 registers: 1028 * Logical Device State Vector 0. LDSV0 1029 * Logical Device State Vector 1. LDSV1 1030 * Logical Device State Vector 2. LDSV2 1031 * Logical Device Group Interrupt Management. LDGIMGN 1032 * 1033 * The first three (LDSVx) are read-only. The 4th register is the 1034 * LDGIMGN, the LDG Interrupt Management register, which is used to 1035 * arm the LDG, or set its timer. 1036 * 1037 * The offset to write to is calculated as follows: 1038 * 1039 * 0x2000 + (VLDG << 4) + offset, where: 1040 * VDLG is the virtual group, i.e., index of the LDG. 1041 * offset is the offset (alignment 8) of the register 1042 * to read or write. 1043 * 1044 * So, for example, if we wanted to arm the first TDC of VRx, we would 1045 * calculate the address as: 1046 * 1047 * 0x2000 + (0 << 4) + 0x18 = 0x18 1048 * 1049 * Context: 1050 * Guest domain 1051 * 1052 */ 1053 void 1054 nxge_hio_ldsv_im( 1055 /* Read any register in the PIO_LDSV space. */ 1056 nxge_t *nxge, 1057 nxge_ldg_t *group, 1058 pio_ld_op_t op, 1059 uint64_t *value) 1060 { 1061 uint64_t offset = VLDG_OFFSET; 1062 1063 offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */ 1064 offset += (op * sizeof (uint64_t)); /* 0, 8, 16, 24 */ 1065 1066 NXGE_REG_RD64(nxge->npi_handle, offset, value); 1067 } 1068 1069 void 1070 nxge_hio_ldgimgn( 1071 /* Write the PIO_LDGIMGN register. */ 1072 nxge_t *nxge, 1073 nxge_ldg_t *group) 1074 { 1075 uint64_t offset = VLDG_OFFSET; 1076 ldgimgm_t mgm; 1077 1078 offset += group->vldg_index << VLDG_SLL; /* bits 7:5 */ 1079 offset += (PIO_LDGIMGN * sizeof (uint64_t)); /* 24 */ 1080 1081 mgm.value = 0; 1082 if (group->arm) { 1083 mgm.bits.ldw.arm = 1; 1084 mgm.bits.ldw.timer = group->ldg_timer; 1085 } else { 1086 mgm.bits.ldw.arm = 0; 1087 mgm.bits.ldw.timer = 0; 1088 } 1089 NXGE_REG_WR64(nxge->npi_handle, offset, mgm.value); 1090 } 1091