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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4 5.0 */ 27 28 #include <sys/types.h> 29 #include <sys/cmn_err.h> 30 #include <sys/conf.h> 31 #include <sys/ddi_impldefs.h> 32 #include <sys/autoconf.h> 33 #include <sys/systm.h> 34 #include <sys/modctl.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/ddi_subrdefs.h> 38 #include <sys/promif.h> 39 #include <sys/machsystm.h> 40 #include <sys/ddi_intr_impl.h> 41 #include <sys/hypervisor_api.h> 42 #include <sys/intr.h> 43 44 #define SUN4V_REG_SPEC2CFG_HDL(x) ((x >> 32) & ~(0xfull << 28)) 45 46 static kmutex_t vnex_id_lock; 47 /* 48 * Vnex name to pil map 49 */ 50 typedef struct vnex_regspec { 51 uint64_t physaddr; 52 uint64_t size; 53 } vnex_regspec_t; 54 55 struct vnex_pil_map { 56 caddr_t name; 57 uint32_t pil; 58 }; 59 60 /* vnex interrupt descriptor */ 61 typedef struct vnex_id { 62 dev_info_t *vid_dip; 63 uint32_t vid_ino; 64 uint64_t vid_ihdl; 65 uint_t (*vid_handler)(); 66 caddr_t vid_arg1; 67 caddr_t vid_arg2; 68 ddi_intr_handle_impl_t *vid_ddi_hdlp; 69 uint64_t vid_cfg_hdl; 70 struct vnex_id *vid_next; 71 } vnex_id_t; 72 73 /* vnex interrupt descriptor list */ 74 static vnex_id_t *vnex_id_list; 75 76 hrtime_t vnex_pending_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */ 77 78 /* 79 * vnex interrupt descriptor list manipulation functions 80 */ 81 82 static vnex_id_t *vnex_locate_id(dev_info_t *dip, uint32_t ino); 83 static vnex_id_t *vnex_alloc_id(dev_info_t *dip, uint32_t ino, 84 uint64_t dhdl); 85 static void vnex_add_id(vnex_id_t *vid_p); 86 static void vnex_rem_id(vnex_id_t *vid_p); 87 static void vnex_free_id(vnex_id_t *vid_p); 88 89 uint_t vnex_intr_wrapper(caddr_t arg); 90 91 static struct vnex_pil_map vnex_name_to_pil[] = { 92 {"console", PIL_12}, 93 {"fma", PIL_5}, 94 {"echo", PIL_3}, 95 {"loop", PIL_3}, 96 {"sunmc", PIL_3}, 97 {"sunvts", PIL_3}, 98 {"explorer", PIL_3}, 99 {"ncp", PIL_8}, 100 {"crypto", PIL_8} 101 }; 102 103 #define VNEX_MAX_DEVS (sizeof (vnex_name_to_pil) / \ 104 sizeof (struct vnex_pil_map)) 105 106 /* 107 * Config information 108 */ 109 static int vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip, 110 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 111 112 static int 113 vnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 114 115 static struct bus_ops vnex_bus_ops = { 116 BUSO_REV, 117 nullbusmap, 118 NULL, /* NO OP */ 119 NULL, /* NO OP */ 120 NULL, /* NO OP */ 121 i_ddi_map_fault, 122 ddi_no_dma_map, 123 ddi_no_dma_allochdl, 124 NULL, 125 NULL, 126 NULL, 127 NULL, 128 NULL, 129 NULL, 130 vnex_ctl, 131 ddi_bus_prop_op, 132 NULL, /* (*bus_get_eventcookie)(); */ 133 NULL, /* (*bus_add_eventcall)(); */ 134 NULL, /* (*bus_remove_eventcall)(); */ 135 NULL, /* (*bus_post_event)(); */ 136 NULL, /* (*bus_intr_ctl)(); */ 137 NULL, /* (*bus_config)(); */ 138 NULL, /* (*bus_unconfig)(); */ 139 NULL, /* (*bus_fm_init)(); */ 140 NULL, /* (*bus_fm_fini)(); */ 141 NULL, /* (*bus_fm_access_enter)(); */ 142 NULL, /* (*bus_fm_access_fini)(); */ 143 NULL, /* (*bus_power)(); */ 144 vnex_intr_ops /* (*bus_intr_op)(); */ 145 }; 146 147 static int vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 148 static int vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 149 150 static struct dev_ops pseudo_ops = { 151 DEVO_REV, /* devo_rev, */ 152 0, /* refcnt */ 153 ddi_no_info, /* info */ 154 nulldev, /* identify */ 155 nulldev, /* probe */ 156 vnex_attach, /* attach */ 157 vnex_detach, /* detach */ 158 nodev, /* reset */ 159 (struct cb_ops *)0, /* driver operations */ 160 &vnex_bus_ops, /* bus operations */ 161 nulldev /* power */ 162 }; 163 164 /* 165 * Module linkage information for the kernel. 166 */ 167 168 static struct modldrv modldrv = { 169 &mod_driverops, /* Type of module. This one is a pseudo driver */ 170 "sun4v virtual-devices nexus driver v%I%", 171 &pseudo_ops, /* driver ops */ 172 }; 173 174 static struct modlinkage modlinkage = { 175 MODREV_1, (void *)&modldrv, NULL 176 }; 177 178 int 179 _init(void) 180 { 181 return (mod_install(&modlinkage)); 182 } 183 184 int 185 _fini(void) 186 { 187 return (mod_remove(&modlinkage)); 188 } 189 190 int 191 _info(struct modinfo *modinfop) 192 { 193 return (mod_info(&modlinkage, modinfop)); 194 } 195 196 /*ARGSUSED*/ 197 void 198 vnex_intr_dist(void *arg) 199 { 200 vnex_id_t *vid_p; 201 uint32_t cpuid; 202 int intr_state; 203 hrtime_t start; 204 205 mutex_enter(&vnex_id_lock); 206 207 for (vid_p = vnex_id_list; vid_p != NULL; 208 vid_p = vid_p->vid_next) { 209 /* 210 * Don't do anything for disabled interrupts. 211 * vnex_enable_intr takes care of redistributing interrupts. 212 */ 213 if ((hvio_intr_getvalid(vid_p->vid_ihdl, 214 &intr_state) == H_EOK) && (intr_state == HV_INTR_NOTVALID)) 215 continue; 216 217 cpuid = intr_dist_cpuid(); 218 219 (void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID); 220 /* 221 * Make a best effort to wait for pending interrupts to finish. 222 * There is not much we can do if we timeout. 223 */ 224 start = gethrtime(); 225 while (!panicstr && 226 (hvio_intr_getstate(vid_p->vid_ihdl, &intr_state) == 227 H_EOK) && (intr_state == HV_INTR_DELIVERED_STATE)) { 228 if (gethrtime() - start > vnex_pending_timeout) { 229 cmn_err(CE_WARN, "vnex_intr_dist: %s%d " 230 "ino 0x%x pending: timedout\n", 231 ddi_driver_name(vid_p->vid_dip), 232 ddi_get_instance(vid_p->vid_dip), 233 vid_p->vid_ino); 234 break; 235 } 236 } 237 (void) hvio_intr_settarget(vid_p->vid_ihdl, cpuid); 238 (void) hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID); 239 } 240 mutex_exit(&vnex_id_lock); 241 } 242 243 static int 244 vnex_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 245 { 246 switch (cmd) { 247 case DDI_ATTACH: 248 /* 249 * Intitialize interrupt descriptor list 250 * and mutex. 251 */ 252 vnex_id_list = NULL; 253 mutex_init(&vnex_id_lock, NULL, MUTEX_DRIVER, NULL); 254 /* 255 * Add interrupt redistribution callback. 256 */ 257 intr_dist_add(vnex_intr_dist, dip); 258 return (DDI_SUCCESS); 259 260 case DDI_RESUME: 261 return (DDI_SUCCESS); 262 263 default: 264 return (DDI_FAILURE); 265 } 266 } 267 268 /*ARGSUSED*/ 269 static int 270 vnex_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 271 { 272 switch (cmd) { 273 case DDI_DETACH: 274 return (DDI_FAILURE); 275 276 case DDI_SUSPEND: 277 return (DDI_SUCCESS); 278 279 default: 280 return (DDI_FAILURE); 281 } 282 } 283 284 static int 285 vnex_ctl(dev_info_t *dip, dev_info_t *rdip, 286 ddi_ctl_enum_t ctlop, void *arg, void *result) 287 { 288 char name[12]; /* enough for a decimal integer */ 289 int reglen; 290 uint32_t *vnex_regspec; 291 292 switch (ctlop) { 293 case DDI_CTLOPS_REPORTDEV: 294 if (rdip == NULL) 295 return (DDI_FAILURE); 296 cmn_err(CE_CONT, "?virtual-device: %s%d\n", 297 ddi_driver_name(rdip), ddi_get_instance(rdip)); 298 return (DDI_SUCCESS); 299 300 case DDI_CTLOPS_INITCHILD: 301 { 302 dev_info_t *child = (dev_info_t *)arg; 303 304 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 305 "reg", (caddr_t)&vnex_regspec, ®len) != DDI_SUCCESS) 306 return (DDI_FAILURE); 307 308 (void) sprintf(name, "%x", *vnex_regspec); 309 ddi_set_name_addr(child, name); 310 ddi_set_parent_data(child, NULL); 311 kmem_free((caddr_t)vnex_regspec, reglen); 312 return (DDI_SUCCESS); 313 314 } 315 316 case DDI_CTLOPS_UNINITCHILD: 317 { 318 dev_info_t *child = (dev_info_t *)arg; 319 320 ddi_set_name_addr(child, NULL); 321 ddi_remove_minor_node(arg, NULL); 322 return (DDI_SUCCESS); 323 } 324 325 /* 326 * These ops correspond to functions that "shouldn't" be called 327 * by a pseudo driver. So we whinge when we're called. 328 */ 329 case DDI_CTLOPS_DMAPMAPC: 330 case DDI_CTLOPS_REPORTINT: 331 case DDI_CTLOPS_REGSIZE: 332 { 333 *((off_t *)result) = 0; 334 return (DDI_SUCCESS); 335 } 336 case DDI_CTLOPS_NREGS: 337 { 338 dev_info_t *child = (dev_info_t *)arg; 339 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 340 "reg", (caddr_t)&vnex_regspec, ®len) != DDI_SUCCESS) 341 return (DDI_FAILURE); 342 *((uint_t *)result) = reglen / sizeof (uint32_t); 343 kmem_free((caddr_t)vnex_regspec, reglen); 344 return (DDI_SUCCESS); 345 } 346 case DDI_CTLOPS_SIDDEV: 347 case DDI_CTLOPS_SLAVEONLY: 348 case DDI_CTLOPS_AFFINITY: 349 case DDI_CTLOPS_IOMIN: 350 case DDI_CTLOPS_POKE: 351 case DDI_CTLOPS_PEEK: 352 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 353 ddi_get_name(dip), ddi_get_instance(dip), 354 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip)); 355 return (DDI_FAILURE); 356 357 /* 358 * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up 359 */ 360 default: 361 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 362 } 363 } 364 365 static int 366 vnex_get_pil(dev_info_t *rdip) 367 { 368 int i; 369 caddr_t name; 370 371 name = ddi_node_name(rdip); 372 for (i = 0; i < VNEX_MAX_DEVS; i++) { 373 if (strcmp(vnex_name_to_pil[i].name, 374 name) == 0) { 375 return (vnex_name_to_pil[i].pil); 376 } 377 } 378 /* 379 * if not found pil is 0 380 */ 381 return (0); 382 } 383 384 static int 385 vnex_enable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp) 386 { 387 vnex_id_t *vid_p; 388 uint32_t cpuid; 389 390 vid_p = vnex_locate_id(rdip, hdlp->ih_vector); 391 392 ASSERT(vid_p != NULL); 393 394 cpuid = intr_dist_cpuid(); 395 396 if ((hvio_intr_settarget(vid_p->vid_ihdl, cpuid)) != H_EOK) { 397 return (DDI_FAILURE); 398 } 399 400 if (hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE) != H_EOK) { 401 return (DDI_FAILURE); 402 } 403 404 if ((hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_VALID)) != H_EOK) { 405 return (DDI_FAILURE); 406 } 407 408 return (DDI_SUCCESS); 409 } 410 411 static int 412 vnex_disable_intr(dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp) 413 { 414 vnex_id_t *vid_p; 415 416 vid_p = vnex_locate_id(rdip, hdlp->ih_vector); 417 418 ASSERT(vid_p != NULL); 419 420 if (hvio_intr_setvalid(vid_p->vid_ihdl, HV_INTR_NOTVALID) != H_EOK) { 421 return (DDI_FAILURE); 422 } 423 424 return (DDI_SUCCESS); 425 } 426 427 int 428 vnex_ino_to_inum(dev_info_t *dip, uint32_t ino) 429 { 430 vnex_id_t *vid_p; 431 ddi_intr_handle_impl_t *hdlp; 432 433 if ((vid_p = vnex_locate_id(dip, ino)) == NULL) 434 return (-1); 435 else if ((hdlp = vid_p->vid_ddi_hdlp) == NULL) 436 return (-1); 437 else 438 return (hdlp->ih_inum); 439 } 440 441 static int 442 vnex_add_intr(dev_info_t *dip, dev_info_t *rdip, 443 ddi_intr_handle_impl_t *hdlp) 444 { 445 int reglen, ret = DDI_SUCCESS; 446 vnex_id_t *vid_p; 447 uint64_t cfg; 448 uint32_t ino; 449 uint64_t ihdl; 450 vnex_regspec_t *reg_p; 451 452 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, 453 DDI_PROP_DONTPASS, "reg", (caddr_t)®_p, 454 ®len) != DDI_SUCCESS) { 455 return (DDI_FAILURE); 456 } 457 458 /* 459 * get the sun4v config handle for this device 460 */ 461 462 cfg = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr); 463 kmem_free(reg_p, reglen); 464 ino = hdlp->ih_vector; 465 466 /* 467 * call hv to get vihdl 468 */ 469 if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK) 470 return (DDI_FAILURE); 471 472 hdlp->ih_vector = ihdl; 473 /* 474 * Allocate a interrupt descriptor (id) with the 475 * the interrupt handler and append it to 476 * the id list. 477 */ 478 479 vid_p = vnex_alloc_id(rdip, ino, cfg); 480 vid_p->vid_ihdl = ihdl; 481 vid_p->vid_handler = hdlp->ih_cb_func; 482 vid_p->vid_arg1 = hdlp->ih_cb_arg1; 483 vid_p->vid_arg2 = hdlp->ih_cb_arg2; 484 vid_p->vid_ddi_hdlp = hdlp; 485 486 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 487 (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL); 488 489 if (hdlp->ih_pri == 0) { 490 hdlp->ih_pri = vnex_get_pil(rdip); 491 } 492 493 ret = i_ddi_add_ivintr(hdlp); 494 if (ret != DDI_SUCCESS) { 495 return (ret); 496 } 497 498 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler, 499 vid_p->vid_arg1, vid_p->vid_arg2); 500 501 return (ret); 502 } 503 504 static int 505 vnex_remove_intr(dev_info_t *rdip, 506 ddi_intr_handle_impl_t *hdlp) 507 { 508 vnex_id_t *vid_p; 509 uint32_t ino; 510 int ret = DDI_SUCCESS; 511 512 ino = hdlp->ih_vector; 513 vid_p = vnex_locate_id(rdip, ino); 514 515 hdlp->ih_vector = vid_p->vid_ihdl; 516 i_ddi_rem_ivintr(hdlp); 517 518 vnex_free_id(vid_p); 519 520 return (ret); 521 } 522 523 static int 524 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip, 525 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result) 526 { 527 int ret = DDI_SUCCESS; 528 529 switch (intr_op) { 530 case DDI_INTROP_GETCAP: 531 *(int *)result = DDI_INTR_FLAG_LEVEL; 532 break; 533 case DDI_INTROP_ALLOC: 534 *(int *)result = hdlp->ih_scratch1; 535 break; 536 case DDI_INTROP_GETPRI: 537 *(int *)result = hdlp->ih_pri ? 538 hdlp->ih_pri : vnex_get_pil(rdip); 539 break; 540 case DDI_INTROP_FREE: 541 break; 542 case DDI_INTROP_SETPRI: 543 break; 544 case DDI_INTROP_ADDISR: 545 ret = vnex_add_intr(dip, rdip, hdlp); 546 break; 547 case DDI_INTROP_REMISR: 548 ret = vnex_remove_intr(rdip, hdlp); 549 break; 550 case DDI_INTROP_ENABLE: 551 ret = vnex_enable_intr(rdip, hdlp); 552 break; 553 case DDI_INTROP_DISABLE: 554 ret = vnex_disable_intr(rdip, hdlp); 555 break; 556 case DDI_INTROP_NINTRS: 557 case DDI_INTROP_NAVAIL: 558 *(int *)result = i_ddi_get_intx_nintrs(rdip); 559 break; 560 case DDI_INTROP_SUPPORTED_TYPES: 561 *(int *)result = i_ddi_get_intx_nintrs(rdip) ? 562 DDI_INTR_TYPE_FIXED : 0; 563 break; 564 default: 565 ret = DDI_ENOTSUP; 566 break; 567 } 568 569 return (ret); 570 } 571 572 vnex_id_t * 573 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl) 574 { 575 vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP); 576 577 vid_p->vid_dip = dip; 578 vid_p->vid_ino = ino; 579 vid_p->vid_cfg_hdl = dhdl; 580 581 mutex_enter(&vnex_id_lock); 582 vnex_add_id(vid_p); 583 mutex_exit(&vnex_id_lock); 584 585 return (vid_p); 586 } 587 588 vnex_id_t * 589 vnex_locate_id(dev_info_t *dip, uint32_t ino) 590 { 591 vnex_id_t *vid_p; 592 593 mutex_enter(&vnex_id_lock); 594 vid_p = vnex_id_list; 595 596 while (vid_p != NULL) { 597 if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) { 598 mutex_exit(&vnex_id_lock); 599 return (vid_p); 600 } 601 vid_p = vid_p->vid_next; 602 } 603 mutex_exit(&vnex_id_lock); 604 return (NULL); 605 } 606 607 static void 608 vnex_free_id(vnex_id_t *vid_p) 609 { 610 mutex_enter(&vnex_id_lock); 611 vnex_rem_id(vid_p); 612 mutex_exit(&vnex_id_lock); 613 614 kmem_free(vid_p, sizeof (*vid_p)); 615 } 616 617 static void 618 vnex_rem_id(vnex_id_t *vid_p) 619 { 620 vnex_id_t *prev_p = vnex_id_list; 621 622 if (vnex_id_list == NULL) 623 cmn_err(CE_PANIC, "vnex: interrupt list empty"); 624 625 if (vid_p == NULL) 626 cmn_err(CE_PANIC, "vnex: no element to remove"); 627 628 if (vnex_id_list == vid_p) { 629 vnex_id_list = vid_p->vid_next; 630 } else { 631 while (prev_p != NULL && prev_p->vid_next != vid_p) 632 prev_p = prev_p->vid_next; 633 634 if (prev_p == NULL) 635 cmn_err(CE_PANIC, "vnex: element %p not in list", 636 (void *) vid_p); 637 638 prev_p->vid_next = vid_p->vid_next; 639 } 640 } 641 642 static void 643 vnex_add_id(vnex_id_t *vid_p) 644 { 645 vid_p->vid_next = vnex_id_list; 646 vnex_id_list = vid_p; 647 } 648 649 uint_t 650 vnex_intr_wrapper(caddr_t arg) 651 { 652 vnex_id_t *vid_p = (vnex_id_t *)arg; 653 int res; 654 uint_t (*handler)(); 655 caddr_t handler_arg1; 656 caddr_t handler_arg2; 657 658 handler = vid_p->vid_handler; 659 handler_arg1 = vid_p->vid_arg1; 660 handler_arg2 = vid_p->vid_arg2; 661 662 res = (*handler)(handler_arg1, handler_arg2); 663 664 (void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE); 665 666 return (res); 667 } 668