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 ino = hdlp->ih_vector; 402 403 /* 404 * call hv to get vihdl 405 */ 406 if (hvio_intr_devino_to_sysino(cfg, ino, &ihdl) != H_EOK) 407 return (DDI_FAILURE); 408 409 hdlp->ih_vector = ihdl; 410 /* 411 * Allocate a interrupt descriptor (id) with the 412 * the interrupt handler and append it to 413 * the id list. 414 */ 415 416 vid_p = vnex_alloc_id(rdip, ino, cfg); 417 vid_p->vid_ihdl = ihdl; 418 vid_p->vid_handler = hdlp->ih_cb_func; 419 vid_p->vid_arg1 = hdlp->ih_cb_arg1; 420 vid_p->vid_arg2 = hdlp->ih_cb_arg2; 421 vid_p->vid_ddi_hdlp = hdlp; 422 423 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 424 (ddi_intr_handler_t *)vnex_intr_wrapper, (caddr_t)vid_p, NULL); 425 426 if (hdlp->ih_pri == 0) { 427 hdlp->ih_pri = vnex_get_pil(rdip); 428 } 429 430 ret = i_ddi_add_ivintr(hdlp); 431 if (ret != DDI_SUCCESS) { 432 return (ret); 433 } 434 435 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, vid_p->vid_handler, 436 vid_p->vid_arg1, vid_p->vid_arg2); 437 438 return (ret); 439 } 440 441 static int 442 vnex_remove_intr(dev_info_t *rdip, 443 ddi_intr_handle_impl_t *hdlp) 444 { 445 vnex_id_t *vid_p; 446 uint32_t ino; 447 int ret = DDI_SUCCESS; 448 449 ino = hdlp->ih_vector; 450 vid_p = vnex_locate_id(rdip, ino); 451 452 hdlp->ih_vector = vid_p->vid_ihdl; 453 i_ddi_rem_ivintr(hdlp); 454 455 vnex_free_id(vid_p); 456 457 return (ret); 458 } 459 460 static int 461 vnex_intr_ops(dev_info_t *dip, dev_info_t *rdip, 462 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result) 463 { 464 ddi_ispec_t *ispecp = (ddi_ispec_t *)hdlp->ih_private; 465 int ret = DDI_SUCCESS; 466 467 switch (intr_op) { 468 case DDI_INTROP_GETCAP: 469 *(int *)result = 0; 470 break; 471 case DDI_INTROP_ALLOC: 472 *(int *)result = hdlp->ih_scratch1; 473 break; 474 case DDI_INTROP_GETPRI: 475 *(int *)result = ispecp->is_pil ? 476 ispecp->is_pil : vnex_get_pil(rdip); 477 break; 478 case DDI_INTROP_FREE: 479 break; 480 case DDI_INTROP_SETPRI: 481 ispecp->is_pil = (*(int *)result); 482 break; 483 case DDI_INTROP_ADDISR: 484 hdlp->ih_vector = *ispecp->is_intr; 485 ret = vnex_add_intr(dip, rdip, hdlp); 486 break; 487 case DDI_INTROP_REMISR: 488 hdlp->ih_vector = *ispecp->is_intr; 489 ret = vnex_remove_intr(rdip, hdlp); 490 break; 491 case DDI_INTROP_ENABLE: 492 hdlp->ih_vector = *ispecp->is_intr; 493 ret = vnex_enable_intr(rdip, hdlp); 494 break; 495 case DDI_INTROP_DISABLE: 496 hdlp->ih_vector = *ispecp->is_intr; 497 ret = vnex_disable_intr(rdip, hdlp); 498 break; 499 case DDI_INTROP_NINTRS: 500 case DDI_INTROP_NAVAIL: 501 *(int *)result = i_ddi_get_nintrs(rdip); 502 break; 503 case DDI_INTROP_SUPPORTED_TYPES: 504 *(int *)result = i_ddi_get_nintrs(rdip) ? 505 DDI_INTR_TYPE_FIXED : 0; 506 break; 507 default: 508 ret = DDI_ENOTSUP; 509 break; 510 } 511 512 return (ret); 513 } 514 515 vnex_id_t * 516 vnex_alloc_id(dev_info_t *dip, uint32_t ino, uint64_t dhdl) 517 { 518 vnex_id_t *vid_p = kmem_alloc(sizeof (vnex_id_t), KM_SLEEP); 519 520 vid_p->vid_dip = dip; 521 vid_p->vid_ino = ino; 522 vid_p->vid_cfg_hdl = dhdl; 523 524 mutex_enter(&vnex_id_lock); 525 vnex_add_id(vid_p); 526 mutex_exit(&vnex_id_lock); 527 528 return (vid_p); 529 } 530 531 vnex_id_t * 532 vnex_locate_id(dev_info_t *dip, uint32_t ino) 533 { 534 vnex_id_t *vid_p; 535 536 mutex_enter(&vnex_id_lock); 537 vid_p = vnex_id_list; 538 539 while (vid_p != NULL) { 540 if (vid_p->vid_dip == dip && vid_p->vid_ino == ino) { 541 mutex_exit(&vnex_id_lock); 542 return (vid_p); 543 } 544 vid_p = vid_p->vid_next; 545 } 546 mutex_exit(&vnex_id_lock); 547 return (NULL); 548 } 549 550 static void 551 vnex_free_id(vnex_id_t *vid_p) 552 { 553 mutex_enter(&vnex_id_lock); 554 vnex_rem_id(vid_p); 555 mutex_exit(&vnex_id_lock); 556 557 kmem_free(vid_p, sizeof (*vid_p)); 558 } 559 560 static void 561 vnex_rem_id(vnex_id_t *vid_p) 562 { 563 if (vid_p->vid_prev == NULL) { 564 vnex_id_list = vid_p->vid_next; 565 vid_p->vid_prev = NULL; 566 } else if (vid_p->vid_next == NULL) { 567 vid_p->vid_prev->vid_next = NULL; 568 } else { 569 vid_p->vid_prev->vid_next = vid_p->vid_next; 570 vid_p->vid_next->vid_prev = vid_p->vid_prev; 571 } 572 } 573 574 static void 575 vnex_add_id(vnex_id_t *vid_p) 576 { 577 if (vnex_id_list == NULL) { 578 vnex_id_list = vid_p; 579 vid_p->vid_next = NULL; 580 vid_p->vid_prev = NULL; 581 return; 582 } 583 /* 584 * We always just add to the front of the list 585 */ 586 vnex_id_list->vid_prev = vid_p; 587 vid_p->vid_next = vnex_id_list; 588 vid_p->vid_prev = NULL; 589 vnex_id_list = vid_p; 590 } 591 592 uint_t 593 vnex_intr_wrapper(caddr_t arg) 594 { 595 vnex_id_t *vid_p = (vnex_id_t *)arg; 596 int res; 597 uint_t (*handler)(); 598 caddr_t handler_arg1; 599 caddr_t handler_arg2; 600 601 handler = vid_p->vid_handler; 602 handler_arg1 = vid_p->vid_arg1; 603 handler_arg2 = vid_p->vid_arg2; 604 605 res = (*handler)(handler_arg1, handler_arg2); 606 607 (void) hvio_intr_setstate(vid_p->vid_ihdl, HV_INTR_IDLE_STATE); 608 609 return (res); 610 } 611