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