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 KMEM_FREE(interrupts->htable, interrupts->intr_size); 734 interrupts->htable = NULL; 735 736 if (nxge->ldgvp == NULL) 737 goto nxge_hio_intr_uninit_exit; 738 739 control = nxge->ldgvp; 740 if (control->ldgp) { 741 KMEM_FREE(control->ldgp, 742 sizeof (nxge_ldg_t) * NXGE_INT_MAX_LDGS); 743 control->ldgp = 0; 744 } 745 746 if (control->ldvp) { 747 KMEM_FREE(control->ldvp, 748 sizeof (nxge_ldv_t) * NXGE_INT_MAX_LDS); 749 control->ldvp = 0; 750 } 751 752 KMEM_FREE(control, sizeof (nxge_ldgv_t)); 753 nxge->ldgvp = NULL; 754 755 nxge_hio_intr_uninit_exit: 756 NXGE_DEBUG_MSG((nxge, HIO_CTL, "<== nxge_hio_intr_uninit")); 757 } 758 759 /* 760 * nxge_hio_tdsv_add 761 * 762 * Add a transmit device interrupt. 763 * 764 * Arguments: 765 * nxge 766 * dc The TDC whose interrupt we're adding 767 * 768 * Notes: 769 * 770 * Context: 771 * Guest domain 772 */ 773 static 774 hv_rv_t 775 nxge_hio_tdsv_add( 776 nxge_t *nxge, 777 nxge_hio_dc_t *dc) 778 { 779 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio; 780 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 781 nxhv_dc_fp_t *tx = &nhd->hio.tx; 782 hv_rv_t hv_rv; 783 784 if (tx->getinfo == 0) { 785 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 786 "nx_hio_tdsv_add: tx->getinfo absent")); 787 return (EINVAL); 788 } 789 790 /* 791 * Get the dma channel information. 792 */ 793 hv_rv = (*tx->getinfo)(dc->cookie, dc->page, &dc->ldg.index, 794 &dc->ldg.ldsv); 795 if (hv_rv != 0) { 796 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 797 "nx_hio_tdsv_add: tx->getinfo failed: %ld", hv_rv)); 798 return (EIO); 799 } 800 801 NXGE_DEBUG_MSG((nxge, HIO_CTL, 802 "nx_hio_tdsv_add: VRgroup = %d, LDSV = %d", 803 (int)dc->ldg.index, (int)dc->ldg.ldsv)); 804 805 if (hardware->tdc.count == 0) { 806 hardware->tdc.start = dc->channel; 807 } 808 809 hardware->tdc.count++; 810 hardware->tdc.owned++; 811 812 /* 813 * In version 1.0 of the hybrid I/O driver, there 814 * are eight interrupt vectors per VR. 815 * 816 * Vectors 0 - 3 are reserved for RDCs. 817 * Vectors 4 - 7 are reserved for TDCs. 818 */ 819 dc->ldg.vector = (dc->ldg.ldsv % 2) + HIO_INTR_BLOCK_SIZE; 820 // Version 1.0 hack only! 821 822 return (0); 823 } 824 825 /* 826 * nxge_hio_rdsv_add 827 * 828 * Add a transmit device interrupt. 829 * 830 * Arguments: 831 * nxge 832 * dc The RDC whose interrupt we're adding 833 * 834 * Notes: 835 * 836 * Context: 837 * Guest domain 838 */ 839 static 840 hv_rv_t 841 nxge_hio_rdsv_add( 842 nxge_t *nxge, 843 nxge_hio_dc_t *dc) 844 { 845 nxge_hio_data_t *nhd = (nxge_hio_data_t *)nxge->nxge_hw_p->hio; 846 nxge_hw_pt_cfg_t *hardware = &nxge->pt_config.hw_config; 847 nxhv_dc_fp_t *rx = &nhd->hio.rx; 848 hv_rv_t hv_rv; 849 850 if (rx->getinfo == 0) { 851 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 852 "nx_hio_tdsv_add: rx->getinfo absent")); 853 return (EINVAL); 854 } 855 856 /* 857 * Get DMA channel information. 858 */ 859 hv_rv = (*rx->getinfo)(dc->cookie, dc->page, &dc->ldg.index, 860 &dc->ldg.ldsv); 861 if (hv_rv != 0) { 862 NXGE_ERROR_MSG((nxge, NXGE_ERR_CTL, 863 "nx_hio_tdsv_add: rx->getinfo failed: %ld", hv_rv)); 864 return (EIO); 865 } 866 867 NXGE_DEBUG_MSG((nxge, HIO_CTL, 868 "nx_hio_rdsv_add: VRgroup = %d, LDSV = %d", 869 (int)dc->ldg.index, (int)dc->ldg.ldsv)); 870 871 if (hardware->max_rdcs == 0) { 872 hardware->start_rdc = dc->channel; 873 hardware->def_rdc = dc->channel; 874 } 875 876 hardware->max_rdcs++; 877 878 /* 879 * In version 1.0 of the hybrid I/O driver, there 880 * are eight interrupt vectors per VR. 881 * 882 * Vectors 0 - 3 are reserved for RDCs. 883 */ 884 dc->ldg.vector = (dc->ldg.ldsv % 2); 885 // Version 1.0 hack only! 886 887 return (0); 888 } 889 890 /* 891 * nxge_hio_ldsv_add 892 * 893 * Add a transmit or receive interrupt. 894 * 895 * Arguments: 896 * nxge 897 * dc The DMA channel whose interrupt we're adding 898 * 899 * Notes: 900 * Guest domains can only add interrupts for DMA channels. 901 * They cannot access the MAC, MIF, or SYSERR interrupts. 902 * 903 * Context: 904 * Guest domain 905 */ 906 hv_rv_t 907 nxge_hio_ldsv_add( 908 nxge_t *nxge, 909 nxge_hio_dc_t *dc) 910 { 911 nxge_ldgv_t *control; 912 nxge_ldg_t *group; 913 nxge_ldv_t *device; 914 hv_rv_t hv_rv; 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 ((hv_rv = nxge_hio_tdsv_add(nxge, dc)) != 0) 920 return (hv_rv); 921 } else { 922 NXGE_DEBUG_MSG((nxge, HIO_CTL, "==> nxge_hio_ldsv_add(RDC %d)", 923 dc->channel)); 924 if ((hv_rv = nxge_hio_rdsv_add(nxge, dc)) != 0) 925 return (hv_rv); 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