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