144961713Sgirish /* 244961713Sgirish * CDDL HEADER START 344961713Sgirish * 444961713Sgirish * The contents of this file are subject to the terms of the 544961713Sgirish * Common Development and Distribution License (the "License"). 644961713Sgirish * You may not use this file except in compliance with the License. 744961713Sgirish * 844961713Sgirish * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 944961713Sgirish * or http://www.opensolaris.org/os/licensing. 1044961713Sgirish * See the License for the specific language governing permissions 1144961713Sgirish * and limitations under the License. 1244961713Sgirish * 1344961713Sgirish * When distributing Covered Code, include this CDDL HEADER in each 1444961713Sgirish * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1544961713Sgirish * If applicable, add the following below this CDDL HEADER, with the 1644961713Sgirish * fields enclosed by brackets "[]" replaced with your own identifying 1744961713Sgirish * information: Portions Copyright [yyyy] [name of copyright owner] 1844961713Sgirish * 1944961713Sgirish * CDDL HEADER END 2044961713Sgirish */ 2144961713Sgirish /* 22*4df55fdeSJanie Lu * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2344961713Sgirish * Use is subject to license terms. 2444961713Sgirish */ 2544961713Sgirish 2644961713Sgirish 2744961713Sgirish /* 2844961713Sgirish * Niagara2 Network Interface Unit (NIU) Nexus Driver 2944961713Sgirish */ 3044961713Sgirish 3144961713Sgirish #include <sys/conf.h> 3244961713Sgirish #include <sys/modctl.h> 3344961713Sgirish #include <sys/ddi_impldefs.h> 3444961713Sgirish #include <sys/ddi_subrdefs.h> 3544961713Sgirish #include <sys/ddi.h> 3644961713Sgirish #include <sys/sunndi.h> 3744961713Sgirish #include <sys/sunddi.h> 3844961713Sgirish #include <sys/open.h> 3944961713Sgirish #include <sys/stat.h> 4044961713Sgirish #include <sys/file.h> 4144961713Sgirish #include <sys/machsystm.h> 4244961713Sgirish #include <sys/hsvc.h> 4344961713Sgirish #include <sys/sdt.h> 4444961713Sgirish #include <sys/hypervisor_api.h> 4544961713Sgirish #include "niumx_var.h" 4644961713Sgirish 478fca0570Sjf static int niumx_fm_init_child(dev_info_t *, dev_info_t *, int, 488fca0570Sjf ddi_iblock_cookie_t *); 4944961713Sgirish static int niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip, 5044961713Sgirish ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 5144961713Sgirish static int niumx_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 5244961713Sgirish static int niumx_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 5344961713Sgirish static int niumx_set_intr(dev_info_t *dip, dev_info_t *rdip, 5444961713Sgirish ddi_intr_handle_impl_t *hdlp, int valid); 5544961713Sgirish static int niumx_add_intr(dev_info_t *dip, dev_info_t *rdip, 5644961713Sgirish ddi_intr_handle_impl_t *hdlp); 5744961713Sgirish static int niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip, 5844961713Sgirish ddi_intr_handle_impl_t *hdlp); 5944961713Sgirish static uint_t niumx_intr_hdlr(void *arg); 6044961713Sgirish static int niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 6144961713Sgirish off_t offset, off_t len, caddr_t *addrp); 6244961713Sgirish static int niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 6344961713Sgirish ddi_dma_attr_t *attrp, 6444961713Sgirish int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep); 6544961713Sgirish static int niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, 6644961713Sgirish ddi_dma_handle_t handlep); 6744961713Sgirish static int niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, 6844961713Sgirish ddi_dma_handle_t handle, ddi_dma_req_t *dmareq, 6944961713Sgirish ddi_dma_cookie_t *cookiep, uint_t *ccountp); 7044961713Sgirish static int niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, 7144961713Sgirish ddi_dma_handle_t handle); 7244961713Sgirish static int niumx_ctlops(dev_info_t *dip, dev_info_t *rdip, 7344961713Sgirish ddi_ctl_enum_t op, void *arg, void *result); 7444961713Sgirish 7544961713Sgirish static struct bus_ops niumx_bus_ops = { 7644961713Sgirish BUSO_REV, 7744961713Sgirish niumx_map, 7844961713Sgirish 0, 7944961713Sgirish 0, 8044961713Sgirish 0, 8144961713Sgirish i_ddi_map_fault, 8244961713Sgirish 0, 8344961713Sgirish niumx_dma_allochdl, 8444961713Sgirish niumx_dma_freehdl, 8544961713Sgirish niumx_dma_bindhdl, 8644961713Sgirish niumx_dma_unbindhdl, 8744961713Sgirish 0, 8844961713Sgirish 0, 8944961713Sgirish 0, 9044961713Sgirish niumx_ctlops, 9144961713Sgirish ddi_bus_prop_op, 9244961713Sgirish 0, /* (*bus_get_eventcookie)(); */ 9344961713Sgirish 0, /* (*bus_add_eventcall)(); */ 9444961713Sgirish 0, /* (*bus_remove_eventcall)(); */ 9544961713Sgirish 0, /* (*bus_post_event)(); */ 9644961713Sgirish 0, /* (*bus_intr_ctl)(); */ 9744961713Sgirish 0, /* (*bus_config)(); */ 9844961713Sgirish 0, /* (*bus_unconfig)(); */ 998fca0570Sjf niumx_fm_init_child, /* (*bus_fm_init)(); */ 10044961713Sgirish 0, /* (*bus_fm_fini)(); */ 10144961713Sgirish 0, /* (*bus_enter)() */ 10244961713Sgirish 0, /* (*bus_exit)() */ 10344961713Sgirish 0, /* (*bus_power)() */ 10444961713Sgirish niumx_intr_ops /* (*bus_intr_op)(); */ 10544961713Sgirish }; 10644961713Sgirish 10744961713Sgirish static struct dev_ops niumx_ops = { 10844961713Sgirish DEVO_REV, /* devo_rev */ 10944961713Sgirish 0, /* refcnt */ 11044961713Sgirish ddi_no_info, /* info */ 11144961713Sgirish nulldev, /* identify */ 11244961713Sgirish 0, /* probe */ 11344961713Sgirish niumx_attach, /* attach */ 11444961713Sgirish niumx_detach, /* detach */ 11544961713Sgirish nulldev, /* reset */ 11644961713Sgirish (struct cb_ops *)0, /* driver operations */ 11744961713Sgirish &niumx_bus_ops, /* bus operations */ 11819397407SSherry Moore 0, /* power */ 11919397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 12044961713Sgirish }; 12144961713Sgirish 12244961713Sgirish /* Module linkage information for the kernel. */ 12344961713Sgirish static struct modldrv modldrv = { 12444961713Sgirish &mod_driverops, /* Type of module */ 12519397407SSherry Moore "NIU Nexus Driver", 12644961713Sgirish &niumx_ops, /* driver ops */ 12744961713Sgirish }; 12844961713Sgirish 12944961713Sgirish static struct modlinkage modlinkage = { 13044961713Sgirish MODREV_1, 13144961713Sgirish (void *)&modldrv, 13244961713Sgirish NULL 13344961713Sgirish }; 13444961713Sgirish 13544961713Sgirish static void *niumx_state; 13644961713Sgirish 13744961713Sgirish /* 13844961713Sgirish * forward function declarations: 13944961713Sgirish */ 14044961713Sgirish static void niumx_removechild(dev_info_t *); 14144961713Sgirish static int niumx_initchild(dev_info_t *child); 14244961713Sgirish 14344961713Sgirish int 14444961713Sgirish _init(void) 14544961713Sgirish { 14644961713Sgirish int e; 147d66f8315Sjb uint64_t mjrnum; 148d66f8315Sjb uint64_t mnrnum; 149d66f8315Sjb 150d66f8315Sjb /* 151d66f8315Sjb * Check HV intr group api versioning. 152d66f8315Sjb * This driver uses the old interrupt routines which are supported 153d66f8315Sjb * in old firmware in the CORE API group and in newer firmware in 154d66f8315Sjb * the INTR API group. Support for these calls will be dropped 155d66f8315Sjb * once the INTR API group major goes to 2. 156d66f8315Sjb */ 157d66f8315Sjb if ((hsvc_version(HSVC_GROUP_INTR, &mjrnum, &mnrnum) == 0) && 158d66f8315Sjb (mjrnum > NIUMX_INTR_MAJOR_VER)) { 159d66f8315Sjb cmn_err(CE_WARN, "niumx: unsupported intr api group: " 160d66f8315Sjb "maj:0x%lx, min:0x%lx", mjrnum, mnrnum); 161d66f8315Sjb return (ENOTSUP); 162d66f8315Sjb } 163d66f8315Sjb 16444961713Sgirish if ((e = ddi_soft_state_init(&niumx_state, sizeof (niumx_devstate_t), 16544961713Sgirish 1)) == 0 && (e = mod_install(&modlinkage)) != 0) 16644961713Sgirish ddi_soft_state_fini(&niumx_state); 16744961713Sgirish return (e); 16844961713Sgirish } 16944961713Sgirish 17044961713Sgirish int 17144961713Sgirish _fini(void) 17244961713Sgirish { 17344961713Sgirish int e; 17444961713Sgirish if ((e = mod_remove(&modlinkage)) == 0) 17544961713Sgirish ddi_soft_state_fini(&niumx_state); 17644961713Sgirish return (e); 17744961713Sgirish } 17844961713Sgirish 17944961713Sgirish int 18044961713Sgirish _info(struct modinfo *modinfop) 18144961713Sgirish { 18244961713Sgirish return (mod_info(&modlinkage, modinfop)); 18344961713Sgirish } 18444961713Sgirish 185c165966dSjf 186c165966dSjf hrtime_t niumx_intr_timeout = 2ull * NANOSEC; /* 2 seconds in nanoseconds */ 187c165966dSjf 1883266dff7Sjf void 1893266dff7Sjf niumx_intr_dist(void *arg) 1903266dff7Sjf { 191*4df55fdeSJanie Lu niumx_devstate_t *niumxds_p = (niumx_devstate_t *)arg; 192*4df55fdeSJanie Lu kmutex_t *lock_p = &niumxds_p->niumx_mutex; 193e778ae44SRaghuram Kothakota int i; 194*4df55fdeSJanie Lu niumx_ih_t *ih_p = niumxds_p->niumx_ihtable; 1953266dff7Sjf 1963266dff7Sjf DBG(DBG_A_INTX, NULL, "niumx_intr_dist entered\n"); 1973266dff7Sjf mutex_enter(lock_p); 198e778ae44SRaghuram Kothakota for (i = 0; i < NIUMX_MAX_INTRS; i++, ih_p++) { 1993266dff7Sjf sysino_t sysino = ih_p->ih_sysino; 2003266dff7Sjf cpuid_t cpuid; 201c165966dSjf int intr_state, state; 202c165966dSjf hrtime_t start; 203c165966dSjf dev_info_t *dip = ih_p->ih_dip; 2043266dff7Sjf if (!sysino || /* sequence is significant */ 2053266dff7Sjf (hvio_intr_getvalid(sysino, &intr_state) != H_EOK) || 2063266dff7Sjf (intr_state == HV_INTR_NOTVALID) || 2073266dff7Sjf (cpuid = intr_dist_cpuid()) == ih_p->ih_cpuid) 2083266dff7Sjf continue; 2093266dff7Sjf 2103266dff7Sjf (void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID); 211c165966dSjf 212c165966dSjf /* check for pending interrupts, busy wait if so */ 213c165966dSjf for (start = gethrtime(); !panicstr && 214c165966dSjf (hvio_intr_getstate(sysino, &state) == H_EOK) && 215c165966dSjf (state == HV_INTR_DELIVERED_STATE); /* */) { 216c165966dSjf if (gethrtime() - start > niumx_intr_timeout) { 217c165966dSjf cmn_err(CE_WARN, "%s%d: niumx_intr_dist: " 218c165966dSjf "pending interrupt (%x,%lx) timedout\n", 219c165966dSjf ddi_driver_name(dip), ddi_get_instance(dip), 220c165966dSjf ih_p->ih_inum, sysino); 221c165966dSjf (void) hvio_intr_setstate(sysino, 222adf6c93bSjf HV_INTR_IDLE_STATE); 223c165966dSjf break; 224c165966dSjf } 225c165966dSjf } 2263266dff7Sjf (void) hvio_intr_settarget(sysino, cpuid); 2273266dff7Sjf (void) hvio_intr_setvalid(sysino, HV_INTR_VALID); 2283266dff7Sjf ih_p->ih_cpuid = cpuid; 2293266dff7Sjf } 2303266dff7Sjf mutex_exit(lock_p); 2313266dff7Sjf } 23244961713Sgirish 23344961713Sgirish static int 23444961713Sgirish niumx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 23544961713Sgirish { 23644961713Sgirish int instance = ddi_get_instance(dip); 23744961713Sgirish niumx_devstate_t *niumxds_p; /* devstate pointer */ 23844961713Sgirish niu_regspec_t *reg_p; 23944961713Sgirish uint_t reglen; 24044961713Sgirish int ret = DDI_SUCCESS; 24144961713Sgirish 24244961713Sgirish switch (cmd) { 24344961713Sgirish case DDI_ATTACH: 24444961713Sgirish if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 245adf6c93bSjf DDI_PROP_DONTPASS, "reg", (int **)®_p, ®len) 246adf6c93bSjf != DDI_PROP_SUCCESS) { 24744961713Sgirish DBG(DBG_ATTACH, dip, "reg lookup failed\n"); 24844961713Sgirish ret = DDI_FAILURE; 24944961713Sgirish goto done; 25044961713Sgirish } 25144961713Sgirish 25244961713Sgirish /* 25344961713Sgirish * Allocate and get soft state structure. 25444961713Sgirish */ 25544961713Sgirish if (ddi_soft_state_zalloc(niumx_state, instance) 256adf6c93bSjf != DDI_SUCCESS) { 25744961713Sgirish ret = DDI_FAILURE; 25844961713Sgirish goto prop_free; 25944961713Sgirish } 26044961713Sgirish niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state, 261adf6c93bSjf instance); 26244961713Sgirish niumxds_p->dip = dip; 26344961713Sgirish mutex_init(&niumxds_p->niumx_mutex, NULL, MUTEX_DRIVER, NULL); 26444961713Sgirish 26544961713Sgirish DBG(DBG_ATTACH, dip, "soft state alloc'd instance = %d, " 266adf6c93bSjf "niumxds_p = %p\n", instance, niumxds_p); 26744961713Sgirish 26844961713Sgirish /* hv devhdl: low 28-bit of 1st "reg" entry's addr.hi */ 26944961713Sgirish niumxds_p->niumx_dev_hdl = (devhandle_t)(reg_p->addr_high & 270adf6c93bSjf NIUMX_DEVHDLE_MASK); 27144961713Sgirish 2723266dff7Sjf /* add interrupt redistribution callback */ 273*4df55fdeSJanie Lu intr_dist_add(niumx_intr_dist, niumxds_p); 2743266dff7Sjf 2758fca0570Sjf niumxds_p->niumx_fm_cap = DDI_FM_EREPORT_CAPABLE; 27614ea4bb7Ssd 27714ea4bb7Ssd ddi_fm_init(niumxds_p->dip, &niumxds_p->niumx_fm_cap, 278adf6c93bSjf &niumxds_p->niumx_fm_ibc); 27914ea4bb7Ssd 28044961713Sgirish ret = DDI_SUCCESS; 28144961713Sgirish goto prop_free; 28244961713Sgirish cleanup: 28344961713Sgirish mutex_destroy(&niumxds_p->niumx_mutex); 28444961713Sgirish ddi_soft_state_free(niumx_state, ddi_get_instance(dip)); 28544961713Sgirish prop_free: 28644961713Sgirish ddi_prop_free(reg_p); 28744961713Sgirish done: 28844961713Sgirish return (ret); 28944961713Sgirish 29044961713Sgirish case DDI_RESUME: 29144961713Sgirish default: 29244961713Sgirish break; 29344961713Sgirish } 29444961713Sgirish return (ret); 29544961713Sgirish } 29644961713Sgirish 29744961713Sgirish static int 29844961713Sgirish niumx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 29944961713Sgirish { 30044961713Sgirish niumx_devstate_t *niumxds_p; 30144961713Sgirish 30244961713Sgirish switch (cmd) { 30344961713Sgirish case DDI_DETACH: 30444961713Sgirish 30544961713Sgirish niumxds_p = (niumx_devstate_t *) 30644961713Sgirish ddi_get_soft_state(niumx_state, ddi_get_instance(dip)); 30744961713Sgirish 308*4df55fdeSJanie Lu intr_dist_rem(niumx_intr_dist, niumxds_p); 30914ea4bb7Ssd ddi_fm_fini(dip); 31044961713Sgirish mutex_destroy(&niumxds_p->niumx_mutex); 31144961713Sgirish ddi_soft_state_free(niumx_state, ddi_get_instance(dip)); 31244961713Sgirish return (DDI_SUCCESS); 31344961713Sgirish 31444961713Sgirish case DDI_SUSPEND: 31544961713Sgirish default: 31644961713Sgirish break; 31744961713Sgirish } 31844961713Sgirish return (DDI_FAILURE); 31944961713Sgirish } 32044961713Sgirish 3218fca0570Sjf 3228fca0570Sjf /* 3238fca0570Sjf * Function used to initialize FMA for our children nodes. Called 3248fca0570Sjf * through pci busops when child node calls ddi_fm_init. 3258fca0570Sjf */ 3268fca0570Sjf /*ARGSUSED*/ 3278fca0570Sjf int 3288fca0570Sjf niumx_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, 3298fca0570Sjf ddi_iblock_cookie_t *ibc_p) 3308fca0570Sjf { 3318fca0570Sjf niumx_devstate_t *niumxds_p = DIP_TO_STATE(dip); 3328fca0570Sjf 3338fca0570Sjf ASSERT(ibc_p != NULL); 3348fca0570Sjf *ibc_p = niumxds_p->niumx_fm_ibc; 3358fca0570Sjf 3368fca0570Sjf return (niumxds_p->niumx_fm_cap); 3378fca0570Sjf } 3388fca0570Sjf 3398fca0570Sjf 34044961713Sgirish /*ARGSUSED*/ 34144961713Sgirish int 34244961713Sgirish niumx_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 34344961713Sgirish off_t offset, off_t len, caddr_t *vaddrp) 34444961713Sgirish { 34544961713Sgirish struct regspec p_regspec; 34644961713Sgirish ddi_map_req_t p_mapreq; 34744961713Sgirish niu_regspec_t *reg_p; 34844961713Sgirish int i, rn = mp->map_obj.rnumber, reglen, rnglen, rngnum, ret; 34944961713Sgirish niumx_ranges_t *rng_p; 35044961713Sgirish 35144961713Sgirish uint32_t reg_begin, rng_begin; 35244961713Sgirish 35344961713Sgirish DBG(DBG_MAP, dip, "%s%d: mapping %s%d reg %d\n", NAMEINST(dip), 354adf6c93bSjf NAMEINST(rdip), rn); 35544961713Sgirish 35644961713Sgirish if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 357adf6c93bSjf "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) 35844961713Sgirish return (DDI_FAILURE); 35944961713Sgirish 36044961713Sgirish if (rn < 0 || (rn >= reglen / sizeof (niu_regspec_t))) { 36144961713Sgirish DBG(DBG_MAP, dip, "rnumber out of range: %d\n", rn); 36244961713Sgirish kmem_free(reg_p, reglen); 36344961713Sgirish return (DDI_ME_RNUMBER_RANGE); 36444961713Sgirish } 36544961713Sgirish 36644961713Sgirish /* build regspec up for parent */ 36744961713Sgirish p_mapreq = *mp; /* dup the whole structure */ 36844961713Sgirish p_mapreq.map_type = DDI_MT_REGSPEC; 36944961713Sgirish p_mapreq.map_obj.rp = &p_regspec; 37044961713Sgirish 37144961713Sgirish if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 372adf6c93bSjf (caddr_t)&rng_p, &rnglen) != DDI_SUCCESS) { 37344961713Sgirish DBG(DBG_MAP, dip, "%s%d: no ranges property\n", 374adf6c93bSjf ddi_driver_name(dip), ddi_get_instance(dip)); 37544961713Sgirish kmem_free(reg_p, reglen); 37644961713Sgirish return (DDI_FAILURE); 37744961713Sgirish } 37844961713Sgirish 37944961713Sgirish /* locate matching ranges record */ 38044961713Sgirish rngnum = rnglen / sizeof (niumx_ranges_t); 38144961713Sgirish for (i = 0, reg_p += rn; i < rngnum; rng_p++, i++) { 38244961713Sgirish if (reg_p->addr_high == rng_p->child_hi) 38344961713Sgirish break; 38444961713Sgirish } 38544961713Sgirish 38644961713Sgirish if (i >= rngnum) { 38744961713Sgirish DBG(DBG_MAP, dip, "ranges record for reg[%d] not found.\n", rn); 38844961713Sgirish ret = DDI_ME_REGSPEC_RANGE; 38944961713Sgirish goto err; 39044961713Sgirish } 39144961713Sgirish 39244961713Sgirish /* 39344961713Sgirish * validate request has matching bus type and within 4G 39444961713Sgirish * limit by comparing addr.hi of "ranges" and child "reg". 39544961713Sgirish */ 39644961713Sgirish 39744961713Sgirish ASSERT(reg_p->size_high == 0); 39844961713Sgirish 39944961713Sgirish rng_begin = rng_p->child_lo; 40044961713Sgirish reg_begin = reg_p->addr_low; 40144961713Sgirish /* check to verify reg bounds are within rng bounds */ 40244961713Sgirish if (reg_begin < rng_begin || (reg_begin + (reg_p->size_low - 1)) > 403adf6c93bSjf (rng_begin + (rng_p->size_lo - 1))) { 40444961713Sgirish DBG(DBG_MAP, dip, "size out of range for reg[%d].\n", rn); 40544961713Sgirish ret = DDI_ME_REGSPEC_RANGE; 40644961713Sgirish goto err; 40744961713Sgirish } 40844961713Sgirish 40944961713Sgirish p_regspec.regspec_bustype = rng_p->parent_hi; 41044961713Sgirish p_regspec.regspec_addr = reg_begin - rng_begin + rng_p->parent_lo; 41144961713Sgirish p_regspec.regspec_size = reg_p->size_low; 41244961713Sgirish DBG(DBG_MAP, dip, "regspec:bus,addr,size = (%x,%x,%x)\n", 413adf6c93bSjf p_regspec.regspec_bustype, p_regspec.regspec_addr, 414adf6c93bSjf p_regspec.regspec_size); 41544961713Sgirish ret = ddi_map(dip, &p_mapreq, 0, 0, vaddrp); 41644961713Sgirish DBG(DBG_MAP, dip, "niumx_map: ret %d.\n", ret); 41744961713Sgirish err: 41844961713Sgirish kmem_free(rng_p - i, rnglen); 41944961713Sgirish kmem_free(reg_p - rn, reglen); 42044961713Sgirish return (ret); 42144961713Sgirish } 42244961713Sgirish 42344961713Sgirish /* 42444961713Sgirish * niumx_ctlops 42544961713Sgirish */ 42644961713Sgirish int 42744961713Sgirish niumx_ctlops(dev_info_t *dip, dev_info_t *rdip, 42844961713Sgirish ddi_ctl_enum_t ctlop, void *arg, void *result) 42944961713Sgirish { 43044961713Sgirish niu_regspec_t *reg_p; 43144961713Sgirish int reglen, totreg; 43244961713Sgirish 43344961713Sgirish DBG(DBG_CTLOPS, dip, "niumx_ctlops ctlop=%d.\n", ctlop); 43444961713Sgirish if (rdip == (dev_info_t *)0) 43544961713Sgirish return (DDI_FAILURE); 43644961713Sgirish 43744961713Sgirish switch (ctlop) { 43844961713Sgirish case DDI_CTLOPS_REPORTDEV: 43944961713Sgirish cmn_err(CE_NOTE, "device: %s@%s, %s%d\n", 44044961713Sgirish ddi_node_name(rdip), ddi_get_name_addr(rdip), 44144961713Sgirish NAMEINST(rdip)); 44244961713Sgirish return (DDI_SUCCESS); 44344961713Sgirish 44444961713Sgirish case DDI_CTLOPS_INITCHILD: 44544961713Sgirish return (niumx_initchild((dev_info_t *)arg)); 44644961713Sgirish 44744961713Sgirish case DDI_CTLOPS_UNINITCHILD: 44844961713Sgirish niumx_removechild((dev_info_t *)arg); 44944961713Sgirish return (DDI_SUCCESS); 45044961713Sgirish 45144961713Sgirish case DDI_CTLOPS_REGSIZE: 45244961713Sgirish case DDI_CTLOPS_NREGS: 45344961713Sgirish /* fall through */ 45444961713Sgirish break; 45544961713Sgirish default: 45644961713Sgirish DBG(DBG_CTLOPS, dip, "just pass to ddi_cltops.\n"); 45744961713Sgirish return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 45844961713Sgirish } 45944961713Sgirish 46044961713Sgirish /* REGSIZE/NREGS */ 46144961713Sgirish 46244961713Sgirish *(int *)result = 0; 46344961713Sgirish 46444961713Sgirish if (ddi_getlongprop(DDI_DEV_T_NONE, rdip, DDI_PROP_DONTPASS | 465adf6c93bSjf DDI_PROP_CANSLEEP, "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) 46644961713Sgirish return (DDI_FAILURE); 46744961713Sgirish 46844961713Sgirish totreg = reglen / sizeof (niu_regspec_t); 46944961713Sgirish if (ctlop == DDI_CTLOPS_NREGS) { 47044961713Sgirish DBG(DBG_CTLOPS, (dev_info_t *)dip, "niumx_ctlops NREGS=%d.\n", 471adf6c93bSjf totreg); 47244961713Sgirish *(int *)result = totreg; 47344961713Sgirish } else if (ctlop == DDI_CTLOPS_REGSIZE) { 47444961713Sgirish int rn; 47544961713Sgirish rn = *(int *)arg; 47644961713Sgirish if (rn >= totreg) { 47744961713Sgirish kmem_free(reg_p, reglen); 47844961713Sgirish return (DDI_FAILURE); 47944961713Sgirish } 48044961713Sgirish *(off_t *)result = (reg_p + rn)->size_low; 48144961713Sgirish DBG(DBG_CTLOPS, (dev_info_t *)dip, "rn = %d, REGSIZE=%x.\n", 482adf6c93bSjf rn, *(off_t *)result); 48344961713Sgirish } 48444961713Sgirish 48544961713Sgirish kmem_free(reg_p, reglen); 48644961713Sgirish return (DDI_SUCCESS); 48744961713Sgirish } 48844961713Sgirish 489adf6c93bSjf /* 490adf6c93bSjf * niumx_name_child 491adf6c93bSjf * 492adf6c93bSjf * This function is called from init_child to name a node. It is 493adf6c93bSjf * also passed as a callback for node merging functions. 494adf6c93bSjf * 495adf6c93bSjf * return value: DDI_SUCCESS, DDI_FAILURE 496adf6c93bSjf */ 49744961713Sgirish static int 498adf6c93bSjf niumx_name_child(dev_info_t *child, char *name, int namelen) 49944961713Sgirish { 50044961713Sgirish niu_regspec_t *r; 50144961713Sgirish uint_t n; 50244961713Sgirish 503adf6c93bSjf DBG(DBG_CHK_MOD, (dev_info_t *)child, "==> niumx_name_child\n"); 504adf6c93bSjf 505adf6c93bSjf if (ndi_dev_is_persistent_node(child) == 0) { 506adf6c93bSjf char **unit_addr; 507adf6c93bSjf 508adf6c93bSjf /* name .conf nodes by "unit-address" property */ 509adf6c93bSjf if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 510adf6c93bSjf DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 511adf6c93bSjf DDI_PROP_SUCCESS) { 512adf6c93bSjf cmn_err(CE_WARN, "cannot name node from %s.conf", 513adf6c93bSjf ddi_driver_name(child)); 514adf6c93bSjf return (DDI_FAILURE); 515adf6c93bSjf } 516adf6c93bSjf if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 517adf6c93bSjf cmn_err(CE_WARN, "unit-address property in %s.conf" 518adf6c93bSjf " not well-formed", ddi_driver_name(child)); 519adf6c93bSjf ddi_prop_free(unit_addr); 520adf6c93bSjf return (DDI_FAILURE); 521adf6c93bSjf } 522adf6c93bSjf 523adf6c93bSjf (void) snprintf(name, namelen, "%s", *unit_addr); 524adf6c93bSjf ddi_prop_free(unit_addr); 525adf6c93bSjf return (DDI_SUCCESS); 526adf6c93bSjf } 527adf6c93bSjf 528adf6c93bSjf /* name hardware nodes by "reg" property */ 52944961713Sgirish if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 53044961713Sgirish "reg", (int **)&r, &n) != DDI_SUCCESS) { 531adf6c93bSjf cmn_err(CE_WARN, "reg property not well-formed"); 53244961713Sgirish return (DDI_FAILURE); 53344961713Sgirish } 534678453a8Sspeer (void) snprintf(name, namelen, "%x", (r[0].addr_high)); 53544961713Sgirish ddi_prop_free(r); 536adf6c93bSjf return (DDI_SUCCESS); 537adf6c93bSjf } 538adf6c93bSjf 539adf6c93bSjf static int 540adf6c93bSjf niumx_initchild(dev_info_t *child) 541adf6c93bSjf { 542adf6c93bSjf char name[MAXNAMELEN]; 543adf6c93bSjf 544adf6c93bSjf DBG(DBG_CHK_MOD, (dev_info_t *)child, "==> niumx_initchild\n"); 545adf6c93bSjf /* 546adf6c93bSjf * Non-peristent nodes indicate a prototype node with per-instance 547adf6c93bSjf * properties to be merged into the real h/w device node. 548adf6c93bSjf */ 549adf6c93bSjf if (ndi_dev_is_persistent_node(child) == 0) { 550adf6c93bSjf niu_regspec_t *r; 551adf6c93bSjf uint_t n; 552adf6c93bSjf 553adf6c93bSjf if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 554adf6c93bSjf DDI_PROP_DONTPASS, "reg", (int **)&r, &n) == 555adf6c93bSjf DDI_SUCCESS) { 556adf6c93bSjf cmn_err(CE_WARN, 557adf6c93bSjf "cannot merge prototype from %s.conf", 558adf6c93bSjf ddi_driver_name(child)); 559adf6c93bSjf ddi_prop_free(r); 560adf6c93bSjf return (DDI_NOT_WELL_FORMED); 561adf6c93bSjf } 562adf6c93bSjf 563adf6c93bSjf if (niumx_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) 564adf6c93bSjf return (DDI_NOT_WELL_FORMED); 565adf6c93bSjf 566adf6c93bSjf ddi_set_name_addr(child, name); 567adf6c93bSjf ddi_set_parent_data(child, NULL); 568adf6c93bSjf 569adf6c93bSjf /* 570adf6c93bSjf * Try to merge the properties from this prototype 571adf6c93bSjf * node into real h/w nodes. 572adf6c93bSjf */ 573adf6c93bSjf if (ndi_merge_node(child, niumx_name_child) == DDI_SUCCESS) { 574adf6c93bSjf /* 575adf6c93bSjf * Merged ok - return failure to remove the node. 576adf6c93bSjf */ 577adf6c93bSjf ddi_set_name_addr(child, NULL); 578adf6c93bSjf return (DDI_FAILURE); 579adf6c93bSjf } 580adf6c93bSjf 581adf6c93bSjf /* 582adf6c93bSjf * The child was not merged into a h/w node, 583adf6c93bSjf * but there's not much we can do with it other 584adf6c93bSjf * than return failure to cause the node to be removed. 585adf6c93bSjf */ 586adf6c93bSjf cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 587adf6c93bSjf ddi_driver_name(child), ddi_get_name_addr(child), 588adf6c93bSjf ddi_driver_name(child)); 589adf6c93bSjf ddi_set_name_addr(child, NULL); 590adf6c93bSjf return (DDI_NOT_WELL_FORMED); 591adf6c93bSjf } 592adf6c93bSjf 593adf6c93bSjf /* 594adf6c93bSjf * Initialize real h/w nodes 595adf6c93bSjf */ 596adf6c93bSjf if (niumx_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) 597adf6c93bSjf return (DDI_FAILURE); 598adf6c93bSjf 59944961713Sgirish ddi_set_name_addr(child, name); 60044961713Sgirish return (DDI_SUCCESS); 60144961713Sgirish } 60244961713Sgirish 60344961713Sgirish static void 60444961713Sgirish niumx_removechild(dev_info_t *dip) 60544961713Sgirish { 60644961713Sgirish ddi_set_name_addr(dip, NULL); 60744961713Sgirish ddi_remove_minor_node(dip, NULL); 60844961713Sgirish impl_rem_dev_props(dip); 60944961713Sgirish } 61044961713Sgirish 61144961713Sgirish 61244961713Sgirish 61344961713Sgirish /* 61444961713Sgirish * bus dma alloc handle entry point: 61544961713Sgirish */ 61644961713Sgirish /*ARGSUSED*/ 61744961713Sgirish int 61844961713Sgirish niumx_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attrp, 61944961713Sgirish int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 62044961713Sgirish { 62144961713Sgirish ddi_dma_impl_t *mp; 62244961713Sgirish int sleep = (waitfp == DDI_DMA_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 62344961713Sgirish 62444961713Sgirish DBG(DBG_DMA_ALLOCH, dip, "rdip=%s%d\n", NAMEINST(rdip)); 62544961713Sgirish 62644961713Sgirish if (attrp->dma_attr_version != DMA_ATTR_V0) { 62744961713Sgirish DBG(DBG_DMA_ALLOCH, (dev_info_t *)dip, "DDI_DMA_BADATTR\n"); 62844961713Sgirish return (DDI_DMA_BADATTR); 62944961713Sgirish } 63044961713Sgirish 63144961713Sgirish /* Caution: we don't use zalloc to enhance performance! */ 63244961713Sgirish if ((mp = kmem_alloc(sizeof (ddi_dma_impl_t), sleep)) == 0) { 63344961713Sgirish DBG(DBG_DMA_ALLOCH, dip, "can't alloc ddi_dma_impl_t\n"); 63444961713Sgirish return (DDI_FAILURE); 63544961713Sgirish } 63644961713Sgirish mp->dmai_rdip = rdip; 63744961713Sgirish mp->dmai_pfnlst = NULL; 63844961713Sgirish mp->dmai_cookie = NULL; 63944961713Sgirish mp->dmai_fault = 0; 64044961713Sgirish mp->dmai_fault_check = NULL; 64144961713Sgirish mp->dmai_fault_notify = NULL; 64244961713Sgirish 64344961713Sgirish mp->dmai_attr = *attrp; /* set requestors attr info */ 64444961713Sgirish 64544961713Sgirish DBG(DBG_DMA_ALLOCH, dip, "mp=%p\n", mp); 64644961713Sgirish 64744961713Sgirish *handlep = (ddi_dma_handle_t)mp; 64844961713Sgirish return (DDI_SUCCESS); 64944961713Sgirish } 65044961713Sgirish 65144961713Sgirish 65244961713Sgirish /* 65344961713Sgirish * bus dma free handle entry point: 65444961713Sgirish */ 65544961713Sgirish /*ARGSUSED*/ 65644961713Sgirish int 65744961713Sgirish niumx_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) 65844961713Sgirish { 65944961713Sgirish ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 66044961713Sgirish 66144961713Sgirish if (mp->dmai_cookie) 66244961713Sgirish kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t)); 66344961713Sgirish kmem_free(mp, sizeof (ddi_dma_impl_t)); 66444961713Sgirish 66544961713Sgirish return (DDI_SUCCESS); 66644961713Sgirish } 66744961713Sgirish 66844961713Sgirish 66944961713Sgirish /* 67044961713Sgirish * bus dma bind handle entry point: 67144961713Sgirish * 67244961713Sgirish * check/enforce DMA type, setup pfn0 and some other key pieces 67344961713Sgirish * of this dma request. 67444961713Sgirish * Note: this only works with DMA_OTYP_VADDR, and makes use of the known 67544961713Sgirish * fact that only contiguous memory blocks will be passed in. 67644961713Sgirish * Therefore only one cookie will ever be returned. 67744961713Sgirish * 67844961713Sgirish * return values: 67944961713Sgirish * DDI_DMA_NOMAPPING - can't get valid pfn0, or bad dma type 68044961713Sgirish * DDI_DMA_NORESOURCES 68144961713Sgirish * DDI_SUCCESS 68244961713Sgirish * 68344961713Sgirish * dma handle members affected (set on exit): 68444961713Sgirish * mp->dmai_object - dmareq->dmar_object 68544961713Sgirish * mp->dmai_rflags - dmareq->dmar_flags 68644961713Sgirish * mp->dmai_pfn0 - 1st page pfn (if va/size pair and not shadow) 68744961713Sgirish * mp->dmai_roffset - initialized to starting page offset 68844961713Sgirish * mp->dmai_size - # of total pages of entire object 68944961713Sgirish * mp->dmai_cookie - new cookie alloc'd 69044961713Sgirish */ 69144961713Sgirish /*ARGSUSED*/ 69244961713Sgirish int 69344961713Sgirish niumx_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, 69444961713Sgirish ddi_dma_handle_t handle, ddi_dma_req_t *dmareq, 69544961713Sgirish ddi_dma_cookie_t *cookiep, uint_t *ccountp) 69644961713Sgirish { 69744961713Sgirish int (*waitfp)(caddr_t) = dmareq->dmar_fp; 69844961713Sgirish ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 69944961713Sgirish ddi_dma_obj_t *dobj_p = &dmareq->dmar_object; 70044961713Sgirish uint32_t offset; 70144961713Sgirish pfn_t pfn0; 70244961713Sgirish int ret; 70344961713Sgirish 70444961713Sgirish DBG(DBG_DMA_BINDH, dip, "rdip=%s%d mp=%p dmareq=%p\n", NAMEINST(rdip), 705adf6c93bSjf mp, dmareq); 70644961713Sgirish 70744961713Sgirish /* first check dma type */ 70844961713Sgirish mp->dmai_rflags = dmareq->dmar_flags & DMP_DDIFLAGS | DMP_NOSYNC; 70944961713Sgirish switch (dobj_p->dmao_type) { 71044961713Sgirish case DMA_OTYP_VADDR: { 71144961713Sgirish caddr_t vaddr = dobj_p->dmao_obj.virt_obj.v_addr; 71244961713Sgirish struct as *as_p = dobj_p->dmao_obj.virt_obj.v_as; 71344961713Sgirish struct hat *hat_p = as_p ? as_p->a_hat : kas.a_hat; 71444961713Sgirish offset = (ulong_t)vaddr & NIUMX_PAGE_OFFSET; 71544961713Sgirish pfn0 = hat_getpfnum(hat_p, vaddr); 71644961713Sgirish } 71744961713Sgirish break; 71844961713Sgirish 71944961713Sgirish case DMA_OTYP_BUFVADDR: 72044961713Sgirish case DMA_OTYP_PAGES: 72144961713Sgirish case DMA_OTYP_PADDR: 72244961713Sgirish default: 72344961713Sgirish cmn_err(CE_WARN, "%s%d requested unsupported dma type %x", 724adf6c93bSjf NAMEINST(mp->dmai_rdip), dobj_p->dmao_type); 72544961713Sgirish ret = DDI_DMA_NOMAPPING; 72644961713Sgirish goto err; 72744961713Sgirish } 72844961713Sgirish if (pfn0 == PFN_INVALID) { 72944961713Sgirish cmn_err(CE_WARN, "%s%d: invalid pfn0 for DMA object %p", 730adf6c93bSjf NAMEINST(dip), (void *)dobj_p); 73144961713Sgirish ret = DDI_DMA_NOMAPPING; 73244961713Sgirish goto err; 73344961713Sgirish } 73444961713Sgirish mp->dmai_object = *dobj_p; /* whole object */ 73544961713Sgirish mp->dmai_pfn0 = (void *)pfn0; /* cache pfn0 */ 73644961713Sgirish mp->dmai_roffset = offset; /* pg0 offset */ 73744961713Sgirish mp->dmai_mapping = mp->dmai_roffset | NIUMX_PTOB(pfn0); 73844961713Sgirish mp->dmai_size = mp->dmai_object.dmao_size; 73944961713Sgirish 74044961713Sgirish DBG(DBG_DMA_BINDH, dip, "check pfn: mp=%p pfn0=%x\n", 741adf6c93bSjf mp, mp->dmai_pfn0); 74244961713Sgirish if (!(mp->dmai_cookie = kmem_zalloc(sizeof (ddi_dma_cookie_t), 743adf6c93bSjf waitfp == DDI_DMA_SLEEP ? KM_SLEEP : KM_NOSLEEP))) { 74444961713Sgirish ret = DDI_DMA_NORESOURCES; 74544961713Sgirish goto err; 74644961713Sgirish } 74744961713Sgirish mp->dmai_cookie->dmac_laddress = mp->dmai_mapping; 74844961713Sgirish mp->dmai_cookie->dmac_size = mp->dmai_size; 74944961713Sgirish *ccountp = 1; 75044961713Sgirish *cookiep = *mp->dmai_cookie; 75144961713Sgirish DBG(DBG_DMA_BINDH, dip, "cookie %" PRIx64 "+%x, count=%d\n", 752adf6c93bSjf cookiep->dmac_address, cookiep->dmac_size, *ccountp); 75344961713Sgirish return (DDI_DMA_MAPPED); 75444961713Sgirish 75544961713Sgirish err: 75644961713Sgirish DBG(DBG_DMA_BINDH, (dev_info_t *)dip, 757adf6c93bSjf "niumx_dma_bindhdl error ret=%d\n", ret); 75844961713Sgirish return (ret); 75944961713Sgirish } 76044961713Sgirish 76144961713Sgirish /* 76244961713Sgirish * bus dma unbind handle entry point: 76344961713Sgirish */ 76444961713Sgirish /*ARGSUSED*/ 76544961713Sgirish int 76644961713Sgirish niumx_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) 76744961713Sgirish { 76844961713Sgirish ddi_dma_impl_t *mp = (ddi_dma_impl_t *)handle; 76944961713Sgirish 77044961713Sgirish DBG(DBG_DMA_UNBINDH, dip, "rdip=%s%d, mp=%p\n", 771adf6c93bSjf ddi_driver_name(rdip), ddi_get_instance(rdip), handle); 77244961713Sgirish if (mp->dmai_cookie) { 77344961713Sgirish kmem_free(mp->dmai_cookie, sizeof (ddi_dma_cookie_t)); 77444961713Sgirish mp->dmai_cookie = NULL; 77544961713Sgirish } 77644961713Sgirish 77744961713Sgirish return (DDI_SUCCESS); 77844961713Sgirish } 77944961713Sgirish 78044961713Sgirish /*ARGSUSED*/ 78144961713Sgirish int 78244961713Sgirish niumx_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 78344961713Sgirish ddi_intr_handle_impl_t *hdlp, void *result) 78444961713Sgirish { 78544961713Sgirish 78644961713Sgirish int ret = DDI_SUCCESS; 78744961713Sgirish 78844961713Sgirish DBG(DBG_INTROPS, dip, "niumx_intr_ops: dip=%p rdip=%p intr_op=%x " 78944961713Sgirish "handle=%p\n", dip, rdip, intr_op, hdlp); 79044961713Sgirish 79144961713Sgirish switch (intr_op) { 79244961713Sgirish 79344961713Sgirish case DDI_INTROP_SUPPORTED_TYPES: 79444961713Sgirish *(int *)result = DDI_INTR_TYPE_FIXED; 79544961713Sgirish break; 79644961713Sgirish case DDI_INTROP_GETCAP: 79744961713Sgirish *(int *)result = DDI_INTR_FLAG_LEVEL; 79844961713Sgirish break; 79944961713Sgirish case DDI_INTROP_SETCAP: 80044961713Sgirish ret = DDI_ENOTSUP; 80144961713Sgirish break; 80244961713Sgirish case DDI_INTROP_ALLOC: 80344961713Sgirish /* scratch1 = count, # of intrs from DDI framework */ 80444961713Sgirish *(int *)result = hdlp->ih_scratch1; 80544961713Sgirish break; 80644961713Sgirish case DDI_INTROP_FREE: 80744961713Sgirish /* Do we need to do anything here? */ 80844961713Sgirish break; 80944961713Sgirish case DDI_INTROP_GETPRI: 81044961713Sgirish *(int *)result = NIUMX_DEFAULT_PIL; 81144961713Sgirish break; 81244961713Sgirish case DDI_INTROP_SETPRI: 81344961713Sgirish ret = DDI_ENOTSUP; 81444961713Sgirish break; 81544961713Sgirish case DDI_INTROP_ADDISR: 81644961713Sgirish ret = niumx_add_intr(dip, rdip, hdlp); 81744961713Sgirish break; 81844961713Sgirish case DDI_INTROP_REMISR: 81944961713Sgirish ret = niumx_rem_intr(dip, rdip, hdlp); 82044961713Sgirish break; 82144961713Sgirish case DDI_INTROP_ENABLE: 82244961713Sgirish ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_VALID); 82344961713Sgirish break; 82444961713Sgirish case DDI_INTROP_DISABLE: 82544961713Sgirish ret = niumx_set_intr(dip, rdip, hdlp, HV_INTR_NOTVALID); 82644961713Sgirish break; 82744961713Sgirish case DDI_INTROP_SETMASK: 82844961713Sgirish ret = DDI_ENOTSUP; 82944961713Sgirish break; 83044961713Sgirish case DDI_INTROP_CLRMASK: 83144961713Sgirish ret = DDI_ENOTSUP; 83244961713Sgirish break; 83344961713Sgirish case DDI_INTROP_GETPENDING: 83444961713Sgirish ret = DDI_ENOTSUP; 83544961713Sgirish break; 83644961713Sgirish case DDI_INTROP_NINTRS: 83744961713Sgirish case DDI_INTROP_NAVAIL: { 83844961713Sgirish devino_t *inos_p; 83944961713Sgirish int inoslen; 84044961713Sgirish if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 841adf6c93bSjf "interrupts", (caddr_t)&inos_p, &inoslen) 842adf6c93bSjf != DDI_SUCCESS) { 843adf6c93bSjf ret = DDI_FAILURE; 844adf6c93bSjf break; 84544961713Sgirish } 84644961713Sgirish *(int *)result = inoslen / sizeof (uint32_t); 84744961713Sgirish kmem_free(inos_p, inoslen); 84844961713Sgirish } 84944961713Sgirish break; 85044961713Sgirish default: 85144961713Sgirish ret = DDI_ENOTSUP; 85244961713Sgirish break; 85344961713Sgirish } 85444961713Sgirish 85544961713Sgirish DBG(DBG_INTROPS, dip, "niumx_intr_ops: ret=%d\n", ret); 85644961713Sgirish return (ret); 85744961713Sgirish } 85844961713Sgirish 85944961713Sgirish int 86044961713Sgirish niumx_set_intr(dev_info_t *dip, dev_info_t *rdip, 86144961713Sgirish ddi_intr_handle_impl_t *hdlp, int valid) 86244961713Sgirish { 86344961713Sgirish niumx_ih_t *ih_p; 86444961713Sgirish devino_t *inos_p; 86544961713Sgirish int inoslen, ret = DDI_SUCCESS; 86644961713Sgirish uint64_t hvret; 867*4df55fdeSJanie Lu niumx_devstate_t *niumxds_p; /* devstate pointer */ 868*4df55fdeSJanie Lu int instance = ddi_get_instance(dip); 869*4df55fdeSJanie Lu 870*4df55fdeSJanie Lu niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state, 871*4df55fdeSJanie Lu instance); 87244961713Sgirish 87344961713Sgirish ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS); 87444961713Sgirish 87544961713Sgirish /* find the appropriate slot from the fixed table */ 87644961713Sgirish if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 877adf6c93bSjf "interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) { 87844961713Sgirish ret = DDI_FAILURE; 87944961713Sgirish goto fail; 88044961713Sgirish } 881*4df55fdeSJanie Lu 882*4df55fdeSJanie Lu ih_p = niumxds_p->niumx_ihtable + inos_p[hdlp->ih_inum]; 8838fca0570Sjf DBG(DBG_A_INTX, dip, "niumx_set_intr: rdip=%s%d, valid=%d %s (%x,%x)\n", 884adf6c93bSjf NAMEINST(rdip), valid, valid ? "enabling" : "disabling", 885adf6c93bSjf ih_p->ih_inum, ih_p->ih_sysino); 88644961713Sgirish 887cb343a2eSspeer if (valid == HV_INTR_VALID) 888cb343a2eSspeer (void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE); 88944961713Sgirish if ((hvret = hvio_intr_setvalid(ih_p->ih_sysino, valid)) 890adf6c93bSjf != H_EOK) { 89144961713Sgirish DBG(DBG_A_INTX, dip, "hvio_intr_setvalid failed, ret 0x%x\n", 892adf6c93bSjf hvret); 89344961713Sgirish ret = DDI_FAILURE; 89444961713Sgirish } 89544961713Sgirish kmem_free(inos_p, inoslen); 89644961713Sgirish fail: 89744961713Sgirish return (ret); 89844961713Sgirish } 89944961713Sgirish 90044961713Sgirish 90144961713Sgirish 90244961713Sgirish /* 90344961713Sgirish * niumx_add_intr: 90444961713Sgirish * 905e778ae44SRaghuram Kothakota * This function is called to register interrupts. 90644961713Sgirish */ 90744961713Sgirish int 90844961713Sgirish niumx_add_intr(dev_info_t *dip, dev_info_t *rdip, 90944961713Sgirish ddi_intr_handle_impl_t *hdlp) 91044961713Sgirish { 91144961713Sgirish niumx_ih_t *ih_p; 91244961713Sgirish int inoslen, ret = DDI_SUCCESS; 91344961713Sgirish uint64_t hvret; 9148fca0570Sjf devino_t *inos_p, ino; /* INO numbers, from "interrupts" prop */ 91544961713Sgirish sysino_t sysino; 916*4df55fdeSJanie Lu niumx_devstate_t *niumxds_p; /* devstate pointer */ 917*4df55fdeSJanie Lu int instance = ddi_get_instance(dip); 918*4df55fdeSJanie Lu 919*4df55fdeSJanie Lu niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state, 920*4df55fdeSJanie Lu instance); 92144961713Sgirish 92244961713Sgirish /* get new ino */ 92344961713Sgirish if (hdlp->ih_inum >= NIUMX_MAX_INTRS) { 92444961713Sgirish DBG(DBG_INTR, dip, "error: inum %d out of range\n", 925adf6c93bSjf hdlp->ih_inum); 92644961713Sgirish ret = DDI_FAILURE; 92744961713Sgirish goto done; 92844961713Sgirish } 92944961713Sgirish if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 930adf6c93bSjf "interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) { 93144961713Sgirish ret = DDI_FAILURE; 93244961713Sgirish goto done; 93344961713Sgirish } 934*4df55fdeSJanie Lu ih_p = niumxds_p->niumx_ihtable + inos_p[hdlp->ih_inum]; 9358fca0570Sjf ino = inos_p[hdlp->ih_inum]; 93644961713Sgirish kmem_free(inos_p, inoslen); 9378fca0570Sjf if ((hvret = hvio_intr_devino_to_sysino(DIP_TO_HANDLE(dip), ino, 938adf6c93bSjf &sysino)) != H_EOK) { 93944961713Sgirish DBG(DBG_INTR, dip, "hvio_intr_devino_to_sysino failed, " 940adf6c93bSjf "ret 0x%x\n", hvret); 94144961713Sgirish ret = DDI_FAILURE; 94244961713Sgirish goto done; 94344961713Sgirish } 94444961713Sgirish ih_p->ih_sysino = sysino; 94544961713Sgirish ih_p->ih_dip = dip; 94644961713Sgirish ih_p->ih_inum = hdlp->ih_inum; 94744961713Sgirish ih_p->ih_hdlr = hdlp->ih_cb_func; 94844961713Sgirish ih_p->ih_arg1 = hdlp->ih_cb_arg1; 94944961713Sgirish ih_p->ih_arg2 = hdlp->ih_cb_arg2; 95044961713Sgirish 95144961713Sgirish DBG(DBG_A_INTX, dip, "niumx_add_intr: rdip=%s%d inum=0x%x " 952adf6c93bSjf "handler=%p arg1=%p arg2=%p, new ih_p = %p\n", NAMEINST(rdip), 953adf6c93bSjf hdlp->ih_inum, hdlp->ih_cb_func, hdlp->ih_cb_arg1, 954adf6c93bSjf hdlp->ih_cb_arg2, ih_p); 95544961713Sgirish 95644961713Sgirish if (hdlp->ih_pri == 0) 95744961713Sgirish hdlp->ih_pri = NIUMX_DEFAULT_PIL; 95844961713Sgirish 95944961713Sgirish /* Save sysino value in hdlp */ 96044961713Sgirish hdlp->ih_vector = ih_p->ih_sysino; 96144961713Sgirish 96244961713Sgirish /* swap in our handler & arg */ 96344961713Sgirish DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, (ddi_intr_handler_t *)niumx_intr_hdlr, 964adf6c93bSjf (void *)ih_p, NULL); 96544961713Sgirish 9668fca0570Sjf DBG(DBG_A_INTX, dip, "for ino %x adding (%x,%x)\n", ino, ih_p->ih_inum, 967adf6c93bSjf ih_p->ih_sysino); 96844961713Sgirish ret = i_ddi_add_ivintr(hdlp); 96944961713Sgirish 97044961713Sgirish /* Restore orig. interrupt handler & args in handle. */ 97144961713Sgirish DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_hdlr, ih_p->ih_arg1, 972adf6c93bSjf ih_p->ih_arg2); 97344961713Sgirish 97444961713Sgirish if (ret != DDI_SUCCESS) { 97544961713Sgirish DBG(DBG_A_INTX, dip, "i_ddi_add_ivintr error ret=%x\n", ret); 97644961713Sgirish goto done; 97744961713Sgirish } 97844961713Sgirish 97944961713Sgirish /* select cpu, saving it for removal */ 98044961713Sgirish ih_p->ih_cpuid = intr_dist_cpuid(); 98144961713Sgirish 98244961713Sgirish if ((hvret = hvio_intr_settarget(ih_p->ih_sysino, ih_p->ih_cpuid)) 983adf6c93bSjf != H_EOK) { 98444961713Sgirish DBG(DBG_A_INTX, dip, "hvio_intr_settarget failed, ret 0x%x\n", 985adf6c93bSjf hvret); 98644961713Sgirish ret = DDI_FAILURE; 98744961713Sgirish } 98844961713Sgirish done: 98944961713Sgirish DBG(DBG_A_INTX, dip, "done, ret = %d, ih_p 0x%p, hdlp 0x%p\n", ih_p, 990adf6c93bSjf hdlp, ret); 99144961713Sgirish return (ret); 99244961713Sgirish } 99344961713Sgirish 99444961713Sgirish /* 99544961713Sgirish * niumx_rem_intr: 99644961713Sgirish * 99744961713Sgirish * This function is called to unregister interrupts. 99844961713Sgirish */ 99944961713Sgirish int 100044961713Sgirish niumx_rem_intr(dev_info_t *dip, dev_info_t *rdip, 100144961713Sgirish ddi_intr_handle_impl_t *hdlp) 100244961713Sgirish { 100344961713Sgirish niumx_ih_t *ih_p; 100444961713Sgirish devino_t *inos_p; 1005c165966dSjf int inoslen, ret = DDI_SUCCESS, state; 1006c165966dSjf hrtime_t start; 1007c165966dSjf sysino_t sysino; 1008*4df55fdeSJanie Lu niumx_devstate_t *niumxds_p; /* devstate pointer */ 1009*4df55fdeSJanie Lu int instance = ddi_get_instance(dip); 1010*4df55fdeSJanie Lu 1011*4df55fdeSJanie Lu niumxds_p = (niumx_devstate_t *)ddi_get_soft_state(niumx_state, 1012*4df55fdeSJanie Lu instance); 101344961713Sgirish 101444961713Sgirish ASSERT(hdlp->ih_inum < NIUMX_MAX_INTRS); 101544961713Sgirish 101644961713Sgirish /* find the appropriate slot from the fixed table */ 101744961713Sgirish if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 1018adf6c93bSjf "interrupts", (caddr_t)&inos_p, &inoslen) != DDI_SUCCESS) { 101944961713Sgirish ret = DDI_FAILURE; 102044961713Sgirish goto fail1; 102144961713Sgirish } 1022*4df55fdeSJanie Lu ih_p = niumxds_p->niumx_ihtable + inos_p[hdlp->ih_inum]; 1023c165966dSjf sysino = ih_p->ih_sysino; 1024c165966dSjf DBG(DBG_R_INTX, dip, "removing (%x,%x)\n", ih_p->ih_inum, sysino); 1025c165966dSjf 1026c165966dSjf (void) hvio_intr_setvalid(sysino, HV_INTR_NOTVALID); 1027c165966dSjf 1028c165966dSjf /* check for pending interrupts, busy wait if so */ 1029c165966dSjf for (start = gethrtime(); !panicstr && 1030c165966dSjf (hvio_intr_getstate(sysino, &state) == H_EOK) && 1031c165966dSjf (state == HV_INTR_DELIVERED_STATE); /* */) { 1032c165966dSjf if (gethrtime() - start > niumx_intr_timeout) { 1033c165966dSjf cmn_err(CE_WARN, "%s%d: niumx_intr_dist: " 1034c165966dSjf "pending interrupt (%x,%lx) timedout\n", 1035c165966dSjf ddi_driver_name(dip), ddi_get_instance(dip), 1036c165966dSjf ih_p->ih_inum, sysino); 1037c165966dSjf ret = DDI_FAILURE; 1038c165966dSjf goto fail2; 1039c165966dSjf } 104044961713Sgirish } 104144961713Sgirish 1042678453a8Sspeer ih_p->ih_sysino = 0; 1043678453a8Sspeer 1044c165966dSjf hdlp->ih_vector = (uint32_t)sysino; 104544961713Sgirish if (hdlp->ih_vector != NULL) i_ddi_rem_ivintr(hdlp); 104644961713Sgirish 104744961713Sgirish fail2: 104844961713Sgirish kmem_free(inos_p, inoslen); 104944961713Sgirish fail1: 105044961713Sgirish return (ret); 105144961713Sgirish } 105244961713Sgirish 105344961713Sgirish /* 105444961713Sgirish * niumx_intr_hdlr (our interrupt handler) 105544961713Sgirish */ 105644961713Sgirish uint_t 105744961713Sgirish niumx_intr_hdlr(void *arg) 105844961713Sgirish { 105944961713Sgirish niumx_ih_t *ih_p = (niumx_ih_t *)arg; 106044961713Sgirish uint_t r; 106144961713Sgirish 106244961713Sgirish DTRACE_PROBE4(interrupt__start, dev_info_t, ih_p->ih_dip, void *, 1063adf6c93bSjf ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, caddr_t, ih_p->ih_arg2); 106444961713Sgirish 106544961713Sgirish r = (*ih_p->ih_hdlr)(ih_p->ih_arg1, ih_p->ih_arg2); 106644961713Sgirish 106744961713Sgirish DTRACE_PROBE4(interrupt__complete, dev_info_t, ih_p->ih_dip, void *, 1068adf6c93bSjf ih_p->ih_hdlr, caddr_t, ih_p->ih_arg1, int, r); 1069cb343a2eSspeer 1070cb343a2eSspeer (void) hvio_intr_setstate(ih_p->ih_sysino, HV_INTR_IDLE_STATE); 107144961713Sgirish return (r); 107244961713Sgirish } 107344961713Sgirish 107444961713Sgirish #ifdef DEBUG 107544961713Sgirish uint64_t niumx_debug_flags = 0; 107644961713Sgirish 107744961713Sgirish static char *niumx_debug_sym [] = { /* same sequence as niumx_debug_bit */ 107844961713Sgirish /* 0 */ "attach", 107944961713Sgirish /* 1 */ "map", 108044961713Sgirish /* 2 */ "nex-ctlops", 108144961713Sgirish /* 3 */ "introps", 108244961713Sgirish /* 4 */ "intr-add", 108344961713Sgirish /* 5 */ "intr-rem", 108444961713Sgirish /* 6 */ "intr", 108544961713Sgirish /* 7 */ "dma-alloc", 108644961713Sgirish /* 8 */ "dma-bind", 108744961713Sgirish /* 9 */ "dma-unbind", 108844961713Sgirish /* 10 */ "chk-dma-mode" 108944961713Sgirish }; 109044961713Sgirish 109144961713Sgirish /*ARGSUSED*/ 109244961713Sgirish void 109344961713Sgirish niumx_dbg(niumx_debug_bit_t bit, dev_info_t *dip, char *fmt, ...) 109444961713Sgirish { 109544961713Sgirish va_list ap; 109644961713Sgirish char msgbuf[1024]; 109744961713Sgirish 109844961713Sgirish if (!(1ull << bit & niumx_debug_flags)) 109944961713Sgirish return; 110044961713Sgirish va_start(ap, fmt); 110144961713Sgirish (void) vsprintf(msgbuf, fmt, ap); 110244961713Sgirish va_end(ap); 110344961713Sgirish cmn_err(CE_NOTE, "%s: %s", niumx_debug_sym[bit], msgbuf); 110444961713Sgirish } 110544961713Sgirish 110644961713Sgirish #endif /* DEBUG */ 1107