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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Niagara2 Network Interface Unit (NIU) Nexus Driver 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/modctl.h> 34 #include <sys/ddi_impldefs.h> 35 #include <sys/ddi_subrdefs.h> 36 #include <sys/ddi.h> 37 #include <sys/sunndi.h> 38 #include <sys/sunddi.h> 39 #include <sys/open.h> 40 #include <sys/stat.h> 41 #include <sys/file.h> 42 #include <sys/machsystm.h> 43 #include <sys/hsvc.h> 44 #include <sys/sdt.h> 45 #include <sys/hypervisor_api.h> 46 #include "niumx_var.h" 47 48 static int niumx_fm_init_child(dev_info_t *, dev_info_t *, int, 49 ddi_iblock_cookie_t *); 50 static int niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip, 51 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 52 static int niumx_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 53 static int niumx_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 54 static int niumx_set_intr(dev_info_t *dip, dev_info_t *rdip, 55 ddi_intr_handle_impl_t *hdlp, int valid); 56 static int niumx_add_intr(dev_info_t *dip, dev_info_t *rdip, 57 ddi_intr_handle_impl_t *hdlp); 58 static int niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip, 59 ddi_intr_handle_impl_t *hdlp); 60 static uint_t niumx_intr_hdlr(void *arg); 61 static int niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 62 off_t offset, off_t len, caddr_t *addrp); 63 static int niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 64 ddi_dma_attr_t *attrp, 65 int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep); 66 static int niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, 67 ddi_dma_handle_t handlep); 68 static int niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, 69 ddi_dma_handle_t handle, ddi_dma_req_t *dmareq, 70 ddi_dma_cookie_t *cookiep, uint_t *ccountp); 71 static int niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, 72 ddi_dma_handle_t handle); 73 static int niumx_ctlops(dev_info_t *dip, dev_info_t *rdip, 74 ddi_ctl_enum_t op, void *arg, void *result); 75 76 static struct bus_ops niumx_bus_ops = { 77 BUSO_REV, 78 niumx_map, 79 0, 80 0, 81 0, 82 i_ddi_map_fault, 83 0, 84 niumx_dma_allochdl, 85 niumx_dma_freehdl, 86 niumx_dma_bindhdl, 87 niumx_dma_unbindhdl, 88 0, 89 0, 90 0, 91 niumx_ctlops, 92 ddi_bus_prop_op, 93 0, /* (*bus_get_eventcookie)(); */ 94 0, /* (*bus_add_eventcall)(); */ 95 0, /* (*bus_remove_eventcall)(); */ 96 0, /* (*bus_post_event)(); */ 97 0, /* (*bus_intr_ctl)(); */ 98 0, /* (*bus_config)(); */ 99 0, /* (*bus_unconfig)(); */ 100 niumx_fm_init_child, /* (*bus_fm_init)(); */ 101 0, /* (*bus_fm_fini)(); */ 102 0, /* (*bus_enter)() */ 103 0, /* (*bus_exit)() */ 104 0, /* (*bus_power)() */ 105 niumx_intr_ops /* (*bus_intr_op)(); */ 106 }; 107 108 static struct dev_ops niumx_ops = { 109 DEVO_REV, /* devo_rev */ 110 0, /* refcnt */ 111 ddi_no_info, /* info */ 112 nulldev, /* identify */ 113 0, /* probe */ 114 niumx_attach, /* attach */ 115 niumx_detach, /* detach */ 116 nulldev, /* reset */ 117 (struct cb_ops *)0, /* driver operations */ 118 &niumx_bus_ops, /* bus operations */ 119 0 120 }; 121 122 /* Module linkage information for the kernel. */ 123 static struct modldrv modldrv = { 124 &mod_driverops, /* Type of module */ 125 "NIU Nexus Driver %I%", 126 &niumx_ops, /* driver ops */ 127 }; 128 129 static struct modlinkage modlinkage = { 130 MODREV_1, 131 (void *)&modldrv, 132 NULL 133 }; 134 135 static void *niumx_state; 136 static niumx_ih_t niumx_ihtable[NIUMX_MAX_INTRS]; 137 138 /* 139 * forward function declarations: 140 */ 141 static void niumx_removechild(dev_info_t *); 142 static int niumx_initchild(dev_info_t *child); 143 144 int 145 _init(void) 146 { 147 int e; 148 if ((e = ddi_soft_state_init(&niumx_state, sizeof (niumx_devstate_t), 149 1)) == 0 && (e = mod_install(&modlinkage)) != 0) 150 ddi_soft_state_fini(&niumx_state); 151 return (e); 152 } 153 154 int 155 _fini(void) 156 { 157 int e; 158 if ((e = mod_remove(&modlinkage)) == 0) 159 ddi_soft_state_fini(&niumx_state); 160 return (e); 161 } 162 163 int 164 _info(struct modinfo *modinfop) 165 { 166 return (mod_info(&modlinkage, modinfop)); 167 } 168 169 170 hrtime_t niumx_intr_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */ 171 172 void 173 niumx_intr_dist(void *arg) 174 { 175 kmutex_t *lock_p = (kmutex_t *)arg; 176 int i = NIUMX_RSVD_INTRS; 177 niumx_ih_t *ih_p = niumx_ihtable + i; 178 179 DBG(DBG_A_INTX, NULL, "niumx_intr_dist entered\n"); 180 mutex_enter(lock_p); 181 for (; i < NIUMX_MAX_INTRS; i++, ih_p++) { 182 sysino_t sysino = ih_p->ih_sysino; 183 cpuid_t cpuid; 184 int intr_state, state; 185 hrtime_t start; 186 dev_info_t *dip = ih_p->ih_dip; 187 if (!sysino || /* sequence is significant */ 188 (hvio_intr_getvalid(sysino, &intr_state) != H_EOK) || 189 (intr_state == HV_INTR_NOTVALID) || 190 (cpuid = intr_dist_cpuid()) == ih_p->ih_cpuid) 191 continue; 192 193 (void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID); 194 195 /* check for pending interrupts, busy wait if so */ 196 for (start = gethrtime(); !panicstr && 197 (hvio_intr_getstate(sysino, &state) == H_EOK) && 198 (state == HV_INTR_DELIVERED_STATE); /* */) { 199 if (gethrtime() - start > niumx_intr_timeout) { 200 cmn_err(CE_WARN, "%s%d: niumx_intr_dist: " 201 "pending interrupt (%x,%lx) timedout\n", 202 ddi_driver_name(dip), ddi_get_instance(dip), 203 ih_p->ih_inum, sysino); 204 (void) hvio_intr_setstate(sysino, 205 HV_INTR_IDLE_STATE); 206 break; 207 } 208 } 209 (void) hvio_intr_settarget(sysino, cpuid); 210 (void) hvio_intr_setvalid(sysino, HV_INTR_VALID); 211 ih_p->ih_cpuid = cpuid; 212 } 213 mutex_exit(lock_p); 214 } 215 216 /* 217 * Hypervisor INTR services information for the NIU nexus driver. 218 */ 219 static uint64_t niumx_intr_min_ver; /* Neg. API minor version */ 220 static hsvc_info_t niumx_hv_intr = { 221 HSVC_REV_1, NULL, HSVC_GROUP_INTR, NIUMX_INTR_MAJOR_VER, 222 NIUMX_INTR_MINOR_VER, "NIUMX" 223 }; 224 225 static int 226 niumx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 227 { 228 int instance = ddi_get_instance(dip); 229 niumx_devstate_t *niumxds_p; /* devstate pointer */ 230 niu_regspec_t *reg_p; 231 uint_t reglen; 232 int ret = DDI_SUCCESS; 233 234 switch (cmd) { 235 case DDI_ATTACH: 236 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 237 DDI_PROP_DONTPASS, "reg", (int **)®_p, ®len) 238 != DDI_PROP_SUCCESS) { 239 DBG(DBG_ATTACH, dip, "reg lookup failed\n"); 240 ret = DDI_FAILURE; 241 goto done; 242 } 243 244 /* 245 * Allocate and get soft state structure. 246 */ 247 if (ddi_soft_state_zalloc(niumx_state, instance) 248 != DDI_SUCCESS) { 249 ret = DDI_FAILURE; 250 goto prop_free; 251 } 252 niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state, 253 instance); 254 niumxds_p->dip = dip; 255 mutex_init(&niumxds_p->niumx_mutex, NULL, MUTEX_DRIVER, NULL); 256 257 DBG(DBG_ATTACH, dip, "soft state alloc'd instance = %d, " 258 "niumxds_p = %p\n", instance, niumxds_p); 259 260 /* 261 * Negotiate the API version for HV INTR services. 262 */ 263 if ((ret = hsvc_register(&niumx_hv_intr, &niumx_intr_min_ver)) 264 != H_EOK) { 265 cmn_err(CE_WARN, "%s: cannot negotiate hypervisor services " 266 "group: 0x%lx major: 0x%lx minor: 0x%lx errno: %d\n", 267 niumx_hv_intr.hsvc_modname, niumx_hv_intr.hsvc_group, 268 niumx_hv_intr.hsvc_major, niumx_hv_intr.hsvc_minor, ret); 269 ret = DDI_FAILURE; 270 goto cleanup; 271 } 272 273 DBG(DBG_ATTACH, dip, "neg. HV API major 0x%lx minor 0x%lx\n", 274 niumx_hv_intr.hsvc_major, niumx_intr_min_ver); 275 276 /* hv devhdl: low 28-bit of 1st "reg" entry's addr.hi */ 277 niumxds_p->niumx_dev_hdl = (devhandle_t)(reg_p->addr_high & 278 NIUMX_DEVHDLE_MASK); 279 280 /* add interrupt redistribution callback */ 281 intr_dist_add(niumx_intr_dist, &niumxds_p->niumx_mutex); 282 283 niumxds_p->niumx_fm_cap = DDI_FM_EREPORT_CAPABLE; 284 285 ddi_fm_init(niumxds_p->dip, &niumxds_p->niumx_fm_cap, 286 &niumxds_p->niumx_fm_ibc); 287 288 ret = DDI_SUCCESS; 289 goto prop_free; 290 cleanup: 291 mutex_destroy(&niumxds_p->niumx_mutex); 292 ddi_soft_state_free(niumx_state, ddi_get_instance(dip)); 293 prop_free: 294 ddi_prop_free(reg_p); 295 done: 296 return (ret); 297 298 case DDI_RESUME: 299 default: 300 break; 301 } 302 return (ret); 303 } 304 305 static int 306 niumx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 307 { 308 niumx_devstate_t *niumxds_p; 309 310 switch (cmd) { 311 case DDI_DETACH: 312 (void) hsvc_unregister(&niumx_hv_intr); 313 314 niumxds_p = (niumx_devstate_t *) 315 ddi_get_soft_state(niumx_state, ddi_get_instance(dip)); 316 317 intr_dist_rem(niumx_intr_dist, &niumxds_p->niumx_mutex); 318 ddi_fm_fini(dip); 319 mutex_destroy(&niumxds_p->niumx_mutex); 320 ddi_soft_state_free(niumx_state, ddi_get_instance(dip)); 321 return (DDI_SUCCESS); 322 323 case DDI_SUSPEND: 324 default: 325 break; 326 } 327 return (DDI_FAILURE); 328 } 329 330 331 /* 332 * Function used to initialize FMA for our children nodes. Called 333 * through pci busops when child node calls ddi_fm_init. 334 */ 335 /*ARGSUSED*/ 336 int 337 niumx_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, 338 ddi_iblock_cookie_t *ibc_p) 339 { 340 niumx_devstate_t *niumxds_p = DIP_TO_STATE(dip); 341 342 ASSERT(ibc_p != NULL); 343 *ibc_p = niumxds_p->niumx_fm_ibc; 344 345 return (niumxds_p->niumx_fm_cap); 346 } 347 348 349 /*ARGSUSED*/ 350 int 351 niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 352 off_t offset, off_t len, caddr_t *vaddrp) 353 { 354 struct regspec p_regspec; 355 ddi_map_req_t p_mapreq; 356 niu_regspec_t *reg_p; 357 int i, rn = mp->map_obj.rnumber, reglen, rnglen, rngnum, ret; 358 niumx_ranges_t *rng_p; 359 360 uint32_t reg_begin, rng_begin; 361 362 DBG(DBG_MAP, dip, "%s%d: mapping %s%d reg %d\n", NAMEINST(dip), 363 NAMEINST(rdip), rn); 364 365 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 366 "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) 367 return (DDI_FAILURE); 368 369 if (rn < 0 || (rn >= reglen / sizeof (niu_regspec_t))) { 370 DBG(DBG_MAP, dip, "rnumber out of range: %d\n", rn); 371 kmem_free(reg_p, reglen); 372 return (DDI_ME_RNUMBER_RANGE); 373 } 374 375 /* build regspec up for parent */ 376 p_mapreq = *mp; /* dup the whole structure */ 377 p_mapreq.map_type = DDI_MT_REGSPEC; 378 p_mapreq.map_obj.rp = &p_regspec; 379 380 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 381 (caddr_t)&rng_p, &rnglen) != DDI_SUCCESS) { 382 DBG(DBG_MAP, dip, "%s%d: no ranges property\n", 383 ddi_driver_name(dip), ddi_get_instance(dip)); 384 kmem_free(reg_p, reglen); 385 return (DDI_FAILURE); 386 } 387 388 /* locate matching ranges record */ 389 rngnum = rnglen / sizeof (niumx_ranges_t); 390 for (i = 0, reg_p += rn; i < rngnum; rng_p++, i++) { 391 if (reg_p->addr_high == rng_p->child_hi) 392 break; 393 } 394 395 if (i >= rngnum) { 396 DBG(DBG_MAP, dip, "ranges record for reg[%d] not found.\n", rn); 397 ret = DDI_ME_REGSPEC_RANGE; 398 goto err; 399 } 400 401 /* 402 * validate request has matching bus type and within 4G 403 * limit by comparing addr.hi of "ranges" and child "reg". 404 */ 405 406 ASSERT(reg_p->size_high == 0); 407 408 rng_begin = rng_p->child_lo; 409 reg_begin = reg_p->addr_low; 410 /* check to verify reg bounds are within rng bounds */ 411 if (reg_begin < rng_begin || (reg_begin + (reg_p->size_low - 1)) > 412 (rng_begin + (rng_p->size_lo - 1))) { 413 DBG(DBG_MAP, dip, "size out of range for reg[%d].\n", rn); 414 ret = DDI_ME_REGSPEC_RANGE; 415 goto err; 416 } 417 418 p_regspec.regspec_bustype = rng_p->parent_hi; 419 p_regspec.regspec_addr = reg_begin - rng_begin + rng_p->parent_lo; 420 p_regspec.regspec_size = reg_p->size_low; 421 DBG(DBG_MAP, dip, "regspec:bus,addr,size = (%x,%x,%x)\n", 422 p_regspec.regspec_bustype, p_regspec.regspec_addr, 423 p_regspec.regspec_size); 424 ret = ddi_map(dip, &p_mapreq, 0, 0, vaddrp); 425 DBG(DBG_MAP, dip, "niumx_map: ret %d.\n", ret); 426 err: 427 kmem_free(rng_p - i, rnglen); 428 kmem_free(reg_p - rn, reglen); 429 return (ret); 430 } 431 432 /* 433 * niumx_ctlops 434 */ 435 int 436 niumx_ctlops(dev_info_t *dip, dev_info_t *rdip, 437 ddi_ctl_enum_t ctlop, void *arg, void *result) 438 { 439 niu_regspec_t *reg_p; 440 int reglen, totreg; 441 442 DBG(DBG_CTLOPS, dip, "niumx_ctlops ctlop=%d.\n", ctlop); 443 if (rdip == (dev_info_t *)0) 444 return (DDI_FAILURE); 445 446 switch (ctlop) { 447 case DDI_CTLOPS_REPORTDEV: 448 cmn_err(CE_NOTE, "device: %s@%s, %s%d\n", 449 ddi_node_name(rdip), ddi_get_name_addr(rdip), 450 NAMEINST(rdip)); 451 return (DDI_SUCCESS); 452 453 case DDI_CTLOPS_INITCHILD: 454 return (niumx_initchild((dev_info_t *)arg)); 455 456 case DDI_CTLOPS_UNINITCHILD: 457 niumx_removechild((dev_info_t *)arg); 458 return (DDI_SUCCESS); 459 460 case DDI_CTLOPS_REGSIZE: 461 case DDI_CTLOPS_NREGS: 462 /* fall through */ 463 break; 464 default: 465 DBG(DBG_CTLOPS, dip, "just pass to ddi_cltops.\n"); 466 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 467 } 468 469 /* REGSIZE/NREGS */ 470 471 *(int *)result = 0; 472 473 if (ddi_getlongprop(DDI_DEV_T_NONE, rdip, DDI_PROP_DONTPASS | 474 DDI_PROP_CANSLEEP, "reg", (caddr_t)®_p, ®len) 475 != DDI_SUCCESS) 476 return (DDI_FAILURE); 477 478 totreg = reglen / sizeof (niu_regspec_t); 479 if (ctlop == DDI_CTLOPS_NREGS) { 480 DBG(DBG_CTLOPS, (dev_info_t *)dip, "niumx_ctlops NREGS=%d.\n", 481 totreg); 482 *(int *)result = totreg; 483 } else if (ctlop == DDI_CTLOPS_REGSIZE) { 484 int rn; 485 rn = *(int *)arg; 486 if (rn >= totreg) { 487 kmem_free(reg_p, reglen); 488 return (DDI_FAILURE); 489 } 490 *(off_t *)result = (reg_p + rn)->size_low; 491 DBG(DBG_CTLOPS, (dev_info_t *)dip, "rn = %d, REGSIZE=%x.\n", 492 rn, *(off_t *)result); 493 } 494 495 kmem_free(reg_p, reglen); 496 return (DDI_SUCCESS); 497 } 498 499 static int 500 niumx_initchild(dev_info_t *child) 501 { 502 char name[MAXNAMELEN]; 503 niu_regspec_t *r; 504 uint_t n; 505 506 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 507 "reg", (int **)&r, &n) != DDI_SUCCESS) { 508 return (DDI_FAILURE); 509 } 510 (void) snprintf(name, MAXNAMELEN, "%x", (r[0].addr_high & 511 NIUMX_FUNC_NUM_MASK)); 512 ddi_prop_free(r); 513 ddi_set_name_addr(child, name); 514 return (DDI_SUCCESS); 515 } 516 517 static void 518 niumx_removechild(dev_info_t *dip) 519 { 520 ddi_set_name_addr(dip, NULL); 521 ddi_remove_minor_node(dip, NULL); 522 impl_rem_dev_props(dip); 523 } 524 525 526 527 /* 528 * bus dma alloc handle entry point: 529 */ 530 /*ARGSUSED*/ 531 int 532 niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attrp, 533 int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 534 { 535 ddi_dma_impl_t *mp; 536 int sleep = (waitfp == DDI_DMA_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 537 538 DBG(DBG_DMA_ALLOCH, dip, "rdip=%s%d\n", NAMEINST(rdip)); 539 540 if (attrp->dma_attr_version != DMA_ATTR_V0) { 541 DBG(DBG_DMA_ALLOCH, (dev_info_t *)dip, "DDI_DMA_BADATTR\n"); 542 return (DDI_DMA_BADATTR); 543 } 544 545 /* Caution: we don't use zalloc to enhance performance! */ 546 if ((mp = kmem_alloc(sizeof (ddi_dma_impl_t), sleep)) == 0) { 547 DBG(DBG_DMA_ALLOCH, dip, "can't alloc ddi_dma_impl_t\n"); 548 return (DDI_FAILURE); 549 } 550 mp->dmai_rdip = rdip; 551 mp->dmai_pfnlst = NULL; 552 mp->dmai_cookie = NULL; 553 mp->dmai_fault = 0; 554 mp->dmai_fault_check = NULL; 555 mp->dmai_fault_notify = NULL; 556 557 mp->dmai_attr = *attrp; /* set requestors attr info */ 558 559 DBG(DBG_DMA_ALLOCH, dip, "mp=%p\n", mp); 560 561 *handlep = (ddi_dma_handle_t)mp; 562 return (DDI_SUCCESS); 563 } 564 565 566 /* 567 * bus dma free handle entry point: 568 */ 569 /*ARGSUSED*/ 570 int 571 niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) 572 { 573 ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 574 575 if (mp->dmai_cookie) 576 kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t)); 577 kmem_free(mp, sizeof (ddi_dma_impl_t)); 578 579 return (DDI_SUCCESS); 580 } 581 582 583 /* 584 * bus dma bind handle entry point: 585 * 586 * check/enforce DMA type, setup pfn0 and some other key pieces 587 * of this dma request. 588 * Note: this only works with DMA_OTYP_VADDR, and makes use of the known 589 * fact that only contiguous memory blocks will be passed in. 590 * Therefore only one cookie will ever be returned. 591 * 592 * return values: 593 * DDI_DMA_NOMAPPING - can't get valid pfn0, or bad dma type 594 * DDI_DMA_NORESOURCES 595 * DDI_SUCCESS 596 * 597 * dma handle members affected (set on exit): 598 * mp->dmai_object - dmareq->dmar_object 599 * mp->dmai_rflags - dmareq->dmar_flags 600 * mp->dmai_pfn0 - 1st page pfn (if va/size pair and not shadow) 601 * mp->dmai_roffset - initialized to starting page offset 602 * mp->dmai_size - # of total pages of entire object 603 * mp->dmai_cookie - new cookie alloc'd 604 */ 605 /*ARGSUSED*/ 606 int 607 niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, 608 ddi_dma_handle_t handle, ddi_dma_req_t *dmareq, 609 ddi_dma_cookie_t *cookiep, uint_t *ccountp) 610 { 611 int (*waitfp)(caddr_t) = dmareq->dmar_fp; 612 ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 613 ddi_dma_obj_t *dobj_p = &dmareq->dmar_object; 614 uint32_t offset; 615 pfn_t pfn0; 616 int ret; 617 618 DBG(DBG_DMA_BINDH, dip, "rdip=%s%d mp=%p dmareq=%p\n", NAMEINST(rdip), 619 mp, dmareq); 620 621 /* first check dma type */ 622 mp->dmai_rflags = dmareq->dmar_flags & DMP_DDIFLAGS | DMP_NOSYNC; 623 switch (dobj_p->dmao_type) { 624 case DMA_OTYP_VADDR: { 625 caddr_t vaddr = dobj_p->dmao_obj.virt_obj.v_addr; 626 struct as *as_p = dobj_p->dmao_obj.virt_obj.v_as; 627 struct hat *hat_p = as_p ? as_p->a_hat : kas.a_hat; 628 offset = (ulong_t)vaddr & NIUMX_PAGE_OFFSET; 629 pfn0 = hat_getpfnum(hat_p, vaddr); 630 } 631 break; 632 633 case DMA_OTYP_BUFVADDR: 634 case DMA_OTYP_PAGES: 635 case DMA_OTYP_PADDR: 636 default: 637 cmn_err(CE_WARN, "%s%d requested unsupported dma type %x", 638 NAMEINST(mp->dmai_rdip), dobj_p->dmao_type); 639 ret = DDI_DMA_NOMAPPING; 640 goto err; 641 } 642 if (pfn0 == PFN_INVALID) { 643 cmn_err(CE_WARN, "%s%d: invalid pfn0 for DMA object %p", 644 NAMEINST(dip), (void *)dobj_p); 645 ret = DDI_DMA_NOMAPPING; 646 goto err; 647 } 648 mp->dmai_object = *dobj_p; /* whole object */ 649 mp->dmai_pfn0 = (void *)pfn0; /* cache pfn0 */ 650 mp->dmai_roffset = offset; /* pg0 offset */ 651 mp->dmai_mapping = mp->dmai_roffset | NIUMX_PTOB(pfn0); 652 mp->dmai_size = mp->dmai_object.dmao_size; 653 654 DBG(DBG_DMA_BINDH, dip, "check pfn: mp=%p pfn0=%x\n", 655 mp, mp->dmai_pfn0); 656 if (!(mp->dmai_cookie = kmem_zalloc(sizeof (ddi_dma_cookie_t), 657 waitfp == DDI_DMA_SLEEP ? KM_SLEEP : KM_NOSLEEP))) { 658 ret = DDI_DMA_NORESOURCES; 659 goto err; 660 } 661 mp->dmai_cookie->dmac_laddress = mp->dmai_mapping; 662 mp->dmai_cookie->dmac_size = mp->dmai_size; 663 *ccountp = 1; 664 *cookiep = *mp->dmai_cookie; 665 DBG(DBG_DMA_BINDH, dip, "cookie %" PRIx64 "+%x, count=%d\n", 666 cookiep->dmac_address, cookiep->dmac_size, *ccountp); 667 return (DDI_DMA_MAPPED); 668 669 err: 670 DBG(DBG_DMA_BINDH, (dev_info_t *)dip, 671 "niumx_dma_bindhdl error ret=%d\n", ret); 672 return (ret); 673 } 674 675 /* 676 * bus dma unbind handle entry point: 677 */ 678 /*ARGSUSED*/ 679 int 680 niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) 681 { 682 ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 683 684 DBG(DBG_DMA_UNBINDH, dip, "rdip=%s%d, mp=%p\n", 685 ddi_driver_name(rdip), ddi_get_instance(rdip), handle); 686 if (mp->dmai_cookie) { 687 kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t)); 688 mp->dmai_cookie = NULL; 689 } 690 691 return (DDI_SUCCESS); 692 } 693 694 /*ARGSUSED*/ 695 int 696 niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 697 ddi_intr_handle_impl_t *hdlp, void *result) 698 { 699 700 int ret = DDI_SUCCESS; 701 702 DBG(DBG_INTROPS, dip, "niumx_intr_ops: dip=%p rdip=%p intr_op=%x " 703 "handle=%p\n", dip, rdip, intr_op, hdlp); 704 705 switch (intr_op) { 706 707 case DDI_INTROP_SUPPORTED_TYPES: 708 *(int *)result = DDI_INTR_TYPE_FIXED; 709 break; 710 case DDI_INTROP_GETCAP: 711 *(int *)result = DDI_INTR_FLAG_LEVEL; 712 break; 713 case DDI_INTROP_SETCAP: 714 ret = DDI_ENOTSUP; 715 break; 716 case DDI_INTROP_ALLOC: 717 /* scratch1 = count, # of intrs from DDI framework */ 718 *(int *)result = hdlp->ih_scratch1; 719 break; 720 case DDI_INTROP_FREE: 721 /* Do we need to do anything here? */ 722 break; 723 case DDI_INTROP_GETPRI: 724 *(int *)result = NIUMX_DEFAULT_PIL; 725 break; 726 case DDI_INTROP_SETPRI: 727 ret = DDI_ENOTSUP; 728 break; 729 case DDI_INTROP_ADDISR: 730 ret = niumx_add_intr(dip, rdip, hdlp); 731 break; 732 case DDI_INTROP_REMISR: 733 ret = niumx_rem_intr(dip, rdip, hdlp); 734 break; 735 case DDI_INTROP_ENABLE: 736 ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_VALID); 737 break; 738 case DDI_INTROP_DISABLE: 739 ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_NOTVALID); 740 break; 741 case DDI_INTROP_SETMASK: 742 ret = DDI_ENOTSUP; 743 break; 744 case DDI_INTROP_CLRMASK: 745 ret = DDI_ENOTSUP; 746 break; 747 case DDI_INTROP_GETPENDING: 748 ret = DDI_ENOTSUP; 749 break; 750 case DDI_INTROP_NINTRS: 751 case DDI_INTROP_NAVAIL: { 752 devino_t *inos_p; 753 int inoslen; 754 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 755 "interrupts", (caddr_t)&inos_p, &inoslen) 756 != DDI_SUCCESS) { 757 ret = DDI_FAILURE; 758 break; 759 } 760 *(int *)result = inoslen / sizeof (uint32_t); 761 kmem_free(inos_p, inoslen); 762 } 763 break; 764 default: 765 ret = DDI_ENOTSUP; 766 break; 767 } 768 769 DBG(DBG_INTROPS, dip, "niumx_intr_ops: ret=%d\n", ret); 770 return (ret); 771 } 772 773 int 774 niumx_set_intr(dev_info_t *dip, dev_info_t *rdip, 775 ddi_intr_handle_impl_t *hdlp, int valid) 776 { 777 niumx_ih_t *ih_p; 778 devino_t *inos_p; 779 int inoslen, ret = DDI_SUCCESS; 780 uint64_t hvret; 781 782 ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS); 783 784 /* find the appropriate slot from the fixed table */ 785 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 786 "interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) { 787 ret = DDI_FAILURE; 788 goto fail; 789 } 790 ih_p = niumx_ihtable + inos_p[hdlp->ih_inum]; 791 DBG(DBG_A_INTX, dip, "niumx_set_intr: rdip=%s%d, valid=%d %s (%x,%x)\n", 792 NAMEINST(rdip), valid, valid ? "enabling" : "disabling", 793 ih_p->ih_inum, ih_p->ih_sysino); 794 795 if (valid == HV_INTR_VALID) 796 (void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE); 797 if ((hvret = hvio_intr_setvalid(ih_p->ih_sysino, valid)) 798 != H_EOK) { 799 DBG(DBG_A_INTX, dip, "hvio_intr_setvalid failed, ret 0x%x\n", 800 hvret); 801 ret = DDI_FAILURE; 802 } 803 kmem_free(inos_p, inoslen); 804 fail: 805 return (ret); 806 } 807 808 809 810 /* 811 * niumx_add_intr: 812 * 813 * This is the leaf/nexus/HV mapping, now read from "interrupts": 814 * 815 * we have a range of 64 to work with: 816 * [0-15] - reserved 817 * [16] - mac0 818 * [17] - MIF 819 * [18] - SYSERR 820 * [19-26] - func0 Rx (qty. 8) 821 * [27-34] - func0 Tx (qty. 8) 822 * [35] - mac1 823 * [36-43] - func1 Rx (qty. 8) 824 * [44-51] - func1 Tx (qty. 8) 825 */ 826 int 827 niumx_add_intr(dev_info_t *dip, dev_info_t *rdip, 828 ddi_intr_handle_impl_t *hdlp) 829 { 830 niumx_ih_t *ih_p; 831 int inoslen, ret = DDI_SUCCESS; 832 uint64_t hvret; 833 devino_t *inos_p, ino; /* INO numbers, from "interrupts" prop */ 834 sysino_t sysino; 835 836 /* get new ino */ 837 if (hdlp->ih_inum >= NIUMX_MAX_INTRS) { 838 DBG(DBG_INTR, dip, "error: inum %d out of range\n", 839 hdlp->ih_inum); 840 ret = DDI_FAILURE; 841 goto done; 842 } 843 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 844 "interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) { 845 ret = DDI_FAILURE; 846 goto done; 847 } 848 ih_p = niumx_ihtable + inos_p[hdlp->ih_inum]; 849 ino = inos_p[hdlp->ih_inum]; 850 kmem_free(inos_p, inoslen); 851 if ((hvret = hvio_intr_devino_to_sysino(DIP_TO_HANDLE(dip), ino, 852 &sysino)) != H_EOK) { 853 DBG(DBG_INTR, dip, "hvio_intr_devino_to_sysino failed, " 854 "ret 0x%x\n", hvret); 855 ret = DDI_FAILURE; 856 goto done; 857 } 858 ih_p->ih_sysino = sysino; 859 ih_p->ih_dip = dip; 860 ih_p->ih_inum = hdlp->ih_inum; 861 ih_p->ih_hdlr = hdlp->ih_cb_func; 862 ih_p->ih_arg1 = hdlp->ih_cb_arg1; 863 ih_p->ih_arg2 = hdlp->ih_cb_arg2; 864 865 DBG(DBG_A_INTX, dip, "niumx_add_intr: rdip=%s%d inum=0x%x " 866 "handler=%p arg1=%p arg2=%p, new ih_p = %p\n", NAMEINST(rdip), 867 hdlp->ih_inum, hdlp->ih_cb_func, hdlp->ih_cb_arg1, 868 hdlp->ih_cb_arg2, ih_p); 869 870 if (hdlp->ih_pri == 0) 871 hdlp->ih_pri = NIUMX_DEFAULT_PIL; 872 873 /* Save sysino value in hdlp */ 874 hdlp->ih_vector = ih_p->ih_sysino; 875 876 /* swap in our handler & arg */ 877 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, (ddi_intr_handler_t *)niumx_intr_hdlr, 878 (void *)ih_p, NULL); 879 880 DBG(DBG_A_INTX, dip, "for ino %x adding (%x,%x)\n", ino, ih_p->ih_inum, 881 ih_p->ih_sysino); 882 ret = i_ddi_add_ivintr(hdlp); 883 884 /* Restore orig. interrupt handler & args in handle. */ 885 DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_hdlr, ih_p->ih_arg1, 886 ih_p->ih_arg2); 887 888 if (ret != DDI_SUCCESS) { 889 DBG(DBG_A_INTX, dip, "i_ddi_add_ivintr error ret=%x\n", ret); 890 goto done; 891 } 892 893 /* select cpu, saving it for removal */ 894 ih_p->ih_cpuid = intr_dist_cpuid(); 895 896 if ((hvret = hvio_intr_settarget(ih_p->ih_sysino, ih_p->ih_cpuid)) 897 != H_EOK) { 898 DBG(DBG_A_INTX, dip, "hvio_intr_settarget failed, ret 0x%x\n", 899 hvret); 900 ret = DDI_FAILURE; 901 } 902 done: 903 DBG(DBG_A_INTX, dip, "done, ret = %d, ih_p 0x%p, hdlp 0x%p\n", ih_p, 904 hdlp, ret); 905 return (ret); 906 } 907 908 /* 909 * niumx_rem_intr: 910 * 911 * This function is called to unregister interrupts. 912 */ 913 int 914 niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip, 915 ddi_intr_handle_impl_t *hdlp) 916 { 917 niumx_ih_t *ih_p; 918 devino_t *inos_p; 919 int inoslen, ret = DDI_SUCCESS, state; 920 hrtime_t start; 921 sysino_t sysino; 922 923 ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS); 924 925 /* find the appropriate slot from the fixed table */ 926 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 927 "interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) { 928 ret = DDI_FAILURE; 929 goto fail1; 930 } 931 ih_p = niumx_ihtable + inos_p[hdlp->ih_inum]; 932 sysino = ih_p->ih_sysino; 933 DBG(DBG_R_INTX, dip, "removing (%x,%x)\n", ih_p->ih_inum, sysino); 934 935 (void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID); 936 937 /* check for pending interrupts, busy wait if so */ 938 for (start = gethrtime(); !panicstr && 939 (hvio_intr_getstate(sysino, &state) == H_EOK) && 940 (state == HV_INTR_DELIVERED_STATE); /* */) { 941 if (gethrtime() - start > niumx_intr_timeout) { 942 cmn_err(CE_WARN, "%s%d: niumx_intr_dist: " 943 "pending interrupt (%x,%lx) timedout\n", 944 ddi_driver_name(dip), ddi_get_instance(dip), 945 ih_p->ih_inum, sysino); 946 ret = DDI_FAILURE; 947 goto fail2; 948 } 949 } 950 951 hdlp->ih_vector = (uint32_t)sysino; 952 if (hdlp->ih_vector != NULL) i_ddi_rem_ivintr(hdlp); 953 954 fail2: 955 kmem_free(inos_p, inoslen); 956 fail1: 957 return (ret); 958 } 959 960 /* 961 * niumx_intr_hdlr (our interrupt handler) 962 */ 963 uint_t 964 niumx_intr_hdlr(void *arg) 965 { 966 niumx_ih_t *ih_p = (niumx_ih_t *)arg; 967 uint_t r; 968 969 DTRACE_PROBE4(interrupt__start, dev_info_t, ih_p->ih_dip, void *, 970 ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, caddr_t, ih_p->ih_arg2); 971 972 r = (*ih_p->ih_hdlr)(ih_p->ih_arg1, ih_p->ih_arg2); 973 974 DTRACE_PROBE4(interrupt__complete, dev_info_t, ih_p->ih_dip, void *, 975 ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, int, r); 976 977 (void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE); 978 return (r); 979 } 980 981 #ifdef DEBUG 982 uint64_t niumx_debug_flags = 0; 983 984 static char *niumx_debug_sym [] = { /* same sequence as niumx_debug_bit */ 985 /* 0 */ "attach", 986 /* 1 */ "map", 987 /* 2 */ "nex-ctlops", 988 /* 3 */ "introps", 989 /* 4 */ "intr-add", 990 /* 5 */ "intr-rem", 991 /* 6 */ "intr", 992 /* 7 */ "dma-alloc", 993 /* 8 */ "dma-bind", 994 /* 9 */ "dma-unbind", 995 /* 10 */ "chk-dma-mode" 996 }; 997 998 /*ARGSUSED*/ 999 void 1000 niumx_dbg(niumx_debug_bit_t bit, dev_info_t *dip, char *fmt, ...) 1001 { 1002 va_list ap; 1003 char msgbuf[1024]; 1004 1005 if (!(1ull << bit & niumx_debug_flags)) 1006 return; 1007 va_start(ap, fmt); 1008 (void) vsprintf(msgbuf, fmt, ap); 1009 va_end(ap); 1010 cmn_err(CE_NOTE, "%s: %s", niumx_debug_sym[bit], msgbuf); 1011 } 1012 1013 #endif /* DEBUG */ 1014