11991Sheppo /*
21991Sheppo * CDDL HEADER START
31991Sheppo *
41991Sheppo * The contents of this file are subject to the terms of the
51991Sheppo * Common Development and Distribution License (the "License").
61991Sheppo * You may not use this file except in compliance with the License.
71991Sheppo *
81991Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91991Sheppo * or http://www.opensolaris.org/os/licensing.
101991Sheppo * See the License for the specific language governing permissions
111991Sheppo * and limitations under the License.
121991Sheppo *
131991Sheppo * When distributing Covered Code, include this CDDL HEADER in each
141991Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151991Sheppo * If applicable, add the following below this CDDL HEADER, with the
161991Sheppo * fields enclosed by brackets "[]" replaced with your own identifying
171991Sheppo * information: Portions Copyright [yyyy] [name of copyright owner]
181991Sheppo *
191991Sheppo * CDDL HEADER END
201991Sheppo */
211991Sheppo /*
226408Sha137994 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
231991Sheppo * Use is subject to license terms.
241991Sheppo */
251991Sheppo
261991Sheppo
271991Sheppo /*
281991Sheppo * Logical domain channel devices are devices implemented entirely
291991Sheppo * in software; cnex is the nexus for channel-devices. They use
301991Sheppo * the HV channel interfaces via the LDC transport module to send
311991Sheppo * and receive data and to register callbacks.
321991Sheppo */
331991Sheppo
341991Sheppo #include <sys/types.h>
351991Sheppo #include <sys/cmn_err.h>
361991Sheppo #include <sys/conf.h>
371991Sheppo #include <sys/ddi.h>
381991Sheppo #include <sys/ddi_impldefs.h>
391991Sheppo #include <sys/devops.h>
401991Sheppo #include <sys/instance.h>
411991Sheppo #include <sys/modctl.h>
421991Sheppo #include <sys/open.h>
431991Sheppo #include <sys/stat.h>
441991Sheppo #include <sys/sunddi.h>
451991Sheppo #include <sys/sunndi.h>
461991Sheppo #include <sys/systm.h>
471991Sheppo #include <sys/mkdev.h>
481991Sheppo #include <sys/machsystm.h>
495513Slm66018 #include <sys/intreg.h>
501991Sheppo #include <sys/intr.h>
511991Sheppo #include <sys/ddi_intr_impl.h>
521991Sheppo #include <sys/ivintr.h>
531991Sheppo #include <sys/hypervisor_api.h>
541991Sheppo #include <sys/ldc.h>
551991Sheppo #include <sys/cnex.h>
561991Sheppo #include <sys/mach_descrip.h>
574423Sjb145095 #include <sys/hsvc.h>
584750Sraghuram #include <sys/sdt.h>
591991Sheppo
601991Sheppo /*
611991Sheppo * Internal functions/information
621991Sheppo */
636408Sha137994 static struct cnex_intr_map cnex_class_to_intr[] = {
646408Sha137994 {LDC_DEV_GENERIC, PIL_3, 0},
656408Sha137994 {LDC_DEV_BLK, PIL_4, 10},
666408Sha137994 {LDC_DEV_BLK_SVC, PIL_3, 10},
676408Sha137994 {LDC_DEV_NT, PIL_6, 35},
686408Sha137994 {LDC_DEV_NT_SVC, PIL_4, 35},
696408Sha137994 {LDC_DEV_SERIAL, PIL_6, 0}
701991Sheppo };
716408Sha137994 #define CNEX_MAX_DEVS (sizeof (cnex_class_to_intr) / \
726408Sha137994 sizeof (cnex_class_to_intr[0]))
736408Sha137994
746408Sha137994 #define CNEX_TX_INTR_WEIGHT 0
751991Sheppo
761991Sheppo #define SUN4V_REG_SPEC2CFG_HDL(x) ((x >> 32) & ~(0xfull << 28))
771991Sheppo
782841Snarayan static clock_t cnex_wait_usecs = 1000; /* wait time in usecs */
793078Snarayan static int cnex_wait_retries = 3;
801991Sheppo static void *cnex_state;
811991Sheppo
821991Sheppo static uint_t cnex_intr_wrapper(caddr_t arg);
834750Sraghuram static dev_info_t *cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id,
844750Sraghuram md_t *mdp, mde_cookie_t mde);
851991Sheppo
861991Sheppo /*
876408Sha137994 * Channel Interrupt Distribution
886408Sha137994 *
896408Sha137994 * In order to balance interrupts among available CPUs, we use
906408Sha137994 * the intr_dist_cpuid_{add,remove}_device_weight() interface to
916408Sha137994 * assign weights to channel interrupts. These weights, which are
926408Sha137994 * defined in the cnex_intr_map structure, influence which CPU
936408Sha137994 * is returned by intr_dist_cpuid() when called via the cnex
946408Sha137994 * interrupt redistribution callback cnex_intr_redist().
956408Sha137994 * Interrupts for VIO devclass channels are given more weight than
966408Sha137994 * other interrupts because they are expected to occur more
976408Sha137994 * frequently and have a larger impact on overall performance.
986408Sha137994 * Transmit interrupts are given a zero weight because they are
996408Sha137994 * not used.
1006408Sha137994 *
1016408Sha137994 * The interrupt weights influence the target CPU selection when
1026408Sha137994 * interrupts are redistributed and when they are added. However,
1036408Sha137994 * removal of interrupts can unbalance the distribution even if
1046408Sha137994 * they are removed in converse order--compared to the order they
1056408Sha137994 * are added. This can occur when interrupts are removed after
1066408Sha137994 * redistribution occurs.
1076408Sha137994 *
1086408Sha137994 * Channel interrupt weights affect interrupt-CPU distribution
1096408Sha137994 * relative to other weighted interrupts on the system. For VIO
1106408Sha137994 * devclass channels, values are chosen to match those used by
1116408Sha137994 * the PCI express nexus driver for net and storage devices.
1126408Sha137994 */
1136408Sha137994 static void cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight);
1146408Sha137994 static int cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo);
1156408Sha137994 static int cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo);
1166408Sha137994 static int32_t cnex_class_weight(ldc_dev_t devclass);
1176408Sha137994
1186408Sha137994 /*
1191991Sheppo * Debug info
1201991Sheppo */
1211991Sheppo #ifdef DEBUG
1221991Sheppo
1231991Sheppo /*
1241991Sheppo * Print debug messages
1251991Sheppo *
1261991Sheppo * set cnexdbg to 0xf for enabling all msgs
1271991Sheppo * 0x8 - Errors
1281991Sheppo * 0x4 - Warnings
1291991Sheppo * 0x2 - All debug messages
1301991Sheppo * 0x1 - Minimal debug messages
1311991Sheppo */
1321991Sheppo
1331991Sheppo int cnexdbg = 0x8;
1341991Sheppo
1351991Sheppo static void
cnexdebug(const char * fmt,...)1361991Sheppo cnexdebug(const char *fmt, ...)
1371991Sheppo {
1381991Sheppo char buf[512];
1391991Sheppo va_list ap;
1401991Sheppo
1411991Sheppo va_start(ap, fmt);
1421991Sheppo (void) vsprintf(buf, fmt, ap);
1431991Sheppo va_end(ap);
1441991Sheppo
1451991Sheppo cmn_err(CE_CONT, "%s\n", buf);
1461991Sheppo }
1471991Sheppo
1481991Sheppo #define D1 \
1491991Sheppo if (cnexdbg & 0x01) \
1501991Sheppo cnexdebug
1511991Sheppo
1521991Sheppo #define D2 \
1531991Sheppo if (cnexdbg & 0x02) \
1541991Sheppo cnexdebug
1551991Sheppo
1561991Sheppo #define DWARN \
1571991Sheppo if (cnexdbg & 0x04) \
1581991Sheppo cnexdebug
1591991Sheppo
1601991Sheppo #define DERR \
1611991Sheppo if (cnexdbg & 0x08) \
1621991Sheppo cnexdebug
1631991Sheppo
1641991Sheppo #else
1651991Sheppo
1661991Sheppo #define D1
1671991Sheppo #define D2
1681991Sheppo #define DWARN
1691991Sheppo #define DERR
1701991Sheppo
1711991Sheppo #endif
1721991Sheppo
1731991Sheppo /*
1741991Sheppo * Config information
1751991Sheppo */
1761991Sheppo static int cnex_attach(dev_info_t *, ddi_attach_cmd_t);
1771991Sheppo static int cnex_detach(dev_info_t *, ddi_detach_cmd_t);
1781991Sheppo static int cnex_open(dev_t *, int, int, cred_t *);
1791991Sheppo static int cnex_close(dev_t, int, int, cred_t *);
1801991Sheppo static int cnex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1811991Sheppo static int cnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
1821991Sheppo void *);
1831991Sheppo
1841991Sheppo static struct bus_ops cnex_bus_ops = {
1851991Sheppo BUSO_REV,
1861991Sheppo nullbusmap, /* bus_map */
1871991Sheppo NULL, /* bus_get_intrspec */
1881991Sheppo NULL, /* bus_add_intrspec */
1891991Sheppo NULL, /* bus_remove_intrspec */
1901991Sheppo i_ddi_map_fault, /* bus_map_fault */
1911991Sheppo ddi_no_dma_map, /* bus_dma_map */
1921991Sheppo ddi_no_dma_allochdl, /* bus_dma_allochdl */
1931991Sheppo NULL, /* bus_dma_freehdl */
1941991Sheppo NULL, /* bus_dma_bindhdl */
1951991Sheppo NULL, /* bus_dma_unbindhdl */
1961991Sheppo NULL, /* bus_dma_flush */
1971991Sheppo NULL, /* bus_dma_win */
1981991Sheppo NULL, /* bus_dma_ctl */
1991991Sheppo cnex_ctl, /* bus_ctl */
2001991Sheppo ddi_bus_prop_op, /* bus_prop_op */
2011991Sheppo 0, /* bus_get_eventcookie */
2021991Sheppo 0, /* bus_add_eventcall */
2031991Sheppo 0, /* bus_remove_eventcall */
2041991Sheppo 0, /* bus_post_event */
2051991Sheppo NULL, /* bus_intr_ctl */
2061991Sheppo NULL, /* bus_config */
2071991Sheppo NULL, /* bus_unconfig */
2081991Sheppo NULL, /* bus_fm_init */
2091991Sheppo NULL, /* bus_fm_fini */
2101991Sheppo NULL, /* bus_fm_access_enter */
2111991Sheppo NULL, /* bus_fm_access_exit */
2121991Sheppo NULL, /* bus_power */
2131991Sheppo NULL /* bus_intr_op */
2141991Sheppo };
2151991Sheppo
2161991Sheppo static struct cb_ops cnex_cb_ops = {
2171991Sheppo cnex_open, /* open */
2181991Sheppo cnex_close, /* close */
2191991Sheppo nodev, /* strategy */
2201991Sheppo nodev, /* print */
2211991Sheppo nodev, /* dump */
2221991Sheppo nodev, /* read */
2231991Sheppo nodev, /* write */
2241991Sheppo cnex_ioctl, /* ioctl */
2251991Sheppo nodev, /* devmap */
2261991Sheppo nodev, /* mmap */
2271991Sheppo nodev, /* segmap */
2281991Sheppo nochpoll, /* poll */
2291991Sheppo ddi_prop_op, /* cb_prop_op */
2301991Sheppo 0, /* streamtab */
2311991Sheppo D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */
2321991Sheppo };
2331991Sheppo
2341991Sheppo static struct dev_ops cnex_ops = {
2351991Sheppo DEVO_REV, /* devo_rev, */
2361991Sheppo 0, /* refcnt */
2371991Sheppo ddi_getinfo_1to1, /* info */
2381991Sheppo nulldev, /* identify */
2391991Sheppo nulldev, /* probe */
2401991Sheppo cnex_attach, /* attach */
2411991Sheppo cnex_detach, /* detach */
2421991Sheppo nodev, /* reset */
2431991Sheppo &cnex_cb_ops, /* driver operations */
2441991Sheppo &cnex_bus_ops, /* bus operations */
2457656SSherry.Moore@Sun.COM nulldev, /* power */
2467656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
2471991Sheppo };
2481991Sheppo
2491991Sheppo /*
2501991Sheppo * Module linkage information for the kernel.
2511991Sheppo */
2521991Sheppo static struct modldrv modldrv = {
2531991Sheppo &mod_driverops,
2547656SSherry.Moore@Sun.COM "sun4v channel-devices nexus",
2551991Sheppo &cnex_ops,
2561991Sheppo };
2571991Sheppo
2581991Sheppo static struct modlinkage modlinkage = {
2591991Sheppo MODREV_1, (void *)&modldrv, NULL
2601991Sheppo };
2611991Sheppo
2621991Sheppo int
_init(void)2631991Sheppo _init(void)
2641991Sheppo {
2651991Sheppo int err;
2664423Sjb145095 uint64_t majornum;
2674423Sjb145095 uint64_t minornum;
2684423Sjb145095
2694423Sjb145095 /*
2704423Sjb145095 * Check HV intr group api versioning.
2714423Sjb145095 * Note that cnex assumes interrupt cookies is
2724423Sjb145095 * in version 1.0 of the intr group api.
2734423Sjb145095 */
2744423Sjb145095 if ((err = hsvc_version(HSVC_GROUP_INTR, &majornum, &minornum)) != 0) {
2754423Sjb145095 cmn_err(CE_WARN, "cnex: failed to get intr api "
2764423Sjb145095 "group versioning errno=%d", err);
2774423Sjb145095 return (err);
2784423Sjb145095 } else if ((majornum != 1) && (majornum != 2)) {
2794423Sjb145095 cmn_err(CE_WARN, "cnex: unsupported intr api group: "
2804423Sjb145095 "maj:0x%lx, min:0x%lx", majornum, minornum);
2814423Sjb145095 return (ENOTSUP);
2824423Sjb145095 }
2831991Sheppo
2841991Sheppo if ((err = ddi_soft_state_init(&cnex_state,
2854750Sraghuram sizeof (cnex_soft_state_t), 0)) != 0) {
2861991Sheppo return (err);
2871991Sheppo }
2881991Sheppo if ((err = mod_install(&modlinkage)) != 0) {
2891991Sheppo ddi_soft_state_fini(&cnex_state);
2901991Sheppo return (err);
2911991Sheppo }
2921991Sheppo return (0);
2931991Sheppo }
2941991Sheppo
2951991Sheppo int
_fini(void)2961991Sheppo _fini(void)
2971991Sheppo {
2981991Sheppo int err;
2991991Sheppo
3001991Sheppo if ((err = mod_remove(&modlinkage)) != 0)
3011991Sheppo return (err);
3021991Sheppo ddi_soft_state_fini(&cnex_state);
3031991Sheppo return (0);
3041991Sheppo }
3051991Sheppo
3061991Sheppo int
_info(struct modinfo * modinfop)3071991Sheppo _info(struct modinfo *modinfop)
3081991Sheppo {
3091991Sheppo return (mod_info(&modlinkage, modinfop));
3101991Sheppo }
3111991Sheppo
3121991Sheppo /*
3131991Sheppo * Callback function invoked by the interrupt redistribution
3141991Sheppo * framework. This will redirect interrupts at CPUs that are
3151991Sheppo * currently available in the system.
3166408Sha137994 *
3176408Sha137994 * Note: any interrupts with weight greater than or equal to
3186408Sha137994 * weight_max must be redistributed when this callback is
3196408Sha137994 * invoked with (weight == weight_max) which will be once per
3206408Sha137994 * redistribution.
3211991Sheppo */
3226408Sha137994 /*ARGSUSED*/
3231991Sheppo static void
cnex_intr_redist(void * arg,int32_t weight_max,int32_t weight)3246408Sha137994 cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight)
3251991Sheppo {
3261991Sheppo cnex_ldc_t *cldcp;
3271991Sheppo cnex_soft_state_t *cnex_ssp = arg;
3281991Sheppo
3291991Sheppo ASSERT(cnex_ssp != NULL);
3301991Sheppo mutex_enter(&cnex_ssp->clist_lock);
3311991Sheppo
3321991Sheppo cldcp = cnex_ssp->clist;
3331991Sheppo while (cldcp != NULL) {
3341991Sheppo
3351991Sheppo mutex_enter(&cldcp->lock);
3361991Sheppo
3376408Sha137994 if (cldcp->tx.hdlr && (cldcp->tx.weight == weight ||
3386408Sha137994 (weight_max == weight && cldcp->tx.weight > weight))) {
3396408Sha137994 (void) cnex_intr_new_cpu(cnex_ssp, &cldcp->tx);
3401991Sheppo }
3411991Sheppo
3426408Sha137994 if (cldcp->rx.hdlr && (cldcp->rx.weight == weight ||
3436408Sha137994 (weight_max == weight && cldcp->rx.weight > weight))) {
3446408Sha137994 (void) cnex_intr_new_cpu(cnex_ssp, &cldcp->rx);
3451991Sheppo }
3461991Sheppo
3471991Sheppo mutex_exit(&cldcp->lock);
3481991Sheppo
3491991Sheppo /* next channel */
3501991Sheppo cldcp = cldcp->next;
3511991Sheppo }
3521991Sheppo
3531991Sheppo mutex_exit(&cnex_ssp->clist_lock);
3541991Sheppo }
3551991Sheppo
3561991Sheppo /*
3576408Sha137994 * Internal function to replace the CPU used by an interrupt
3586408Sha137994 * during interrupt redistribution.
3596408Sha137994 */
3606408Sha137994 static int
cnex_intr_new_cpu(cnex_soft_state_t * ssp,cnex_intr_t * iinfo)3616408Sha137994 cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo)
3626408Sha137994 {
3636408Sha137994 int intr_state;
3646408Sha137994 int rv;
3656408Sha137994
3666408Sha137994 /* Determine if the interrupt is enabled */
3676408Sha137994 rv = hvldc_intr_getvalid(ssp->cfghdl, iinfo->ino, &intr_state);
3686408Sha137994 if (rv) {
3696408Sha137994 DWARN("cnex_intr_new_cpu: rx ino=0x%llx, can't get valid\n",
3706408Sha137994 iinfo->ino);
3716408Sha137994 return (rv);
3726408Sha137994 }
3736408Sha137994
3746408Sha137994 /* If it is enabled, disable it */
3756408Sha137994 if (intr_state == HV_INTR_VALID) {
3766408Sha137994 rv = cnex_intr_dis_wait(ssp, iinfo);
3776408Sha137994 if (rv) {
3786408Sha137994 return (rv);
3796408Sha137994 }
3806408Sha137994 }
3816408Sha137994
3826408Sha137994 /* Target the interrupt at a new CPU. */
3836408Sha137994 iinfo->cpuid = intr_dist_cpuid();
3846408Sha137994 (void) hvldc_intr_settarget(ssp->cfghdl, iinfo->ino, iinfo->cpuid);
3856408Sha137994 intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip,
3866408Sha137994 iinfo->weight);
3876408Sha137994
3886408Sha137994 /* Re-enable the interrupt if it was enabled */
3896408Sha137994 if (intr_state == HV_INTR_VALID) {
3906408Sha137994 (void) hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino,
3916408Sha137994 HV_INTR_VALID);
3926408Sha137994 }
3936408Sha137994
3946408Sha137994 return (0);
3956408Sha137994 }
3966408Sha137994
3976408Sha137994 /*
3986408Sha137994 * Internal function to disable an interrupt and wait
3996408Sha137994 * for any pending interrupts to finish.
4006408Sha137994 */
4016408Sha137994 static int
cnex_intr_dis_wait(cnex_soft_state_t * ssp,cnex_intr_t * iinfo)4026408Sha137994 cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo)
4036408Sha137994 {
4046408Sha137994 int rv, intr_state, retries;
4056408Sha137994
4066408Sha137994 /* disable interrupts */
4076408Sha137994 rv = hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino, HV_INTR_NOTVALID);
4086408Sha137994 if (rv) {
4096408Sha137994 DWARN("cnex_intr_dis_wait: ino=0x%llx, can't set valid\n",
4106408Sha137994 iinfo->ino);
4116408Sha137994 return (ENXIO);
4126408Sha137994 }
4136408Sha137994
4146408Sha137994 /*
4156408Sha137994 * Make a best effort to wait for pending interrupts
4166408Sha137994 * to finish. There is not much we can do if we timeout.
4176408Sha137994 */
4186408Sha137994 retries = 0;
4196408Sha137994
4206408Sha137994 do {
4216408Sha137994 rv = hvldc_intr_getstate(ssp->cfghdl, iinfo->ino, &intr_state);
4226408Sha137994 if (rv) {
4236408Sha137994 DWARN("cnex_intr_dis_wait: ino=0x%llx, can't get "
4246408Sha137994 "state\n", iinfo->ino);
4256408Sha137994 return (ENXIO);
4266408Sha137994 }
4276408Sha137994
4286408Sha137994 if (intr_state != HV_INTR_DELIVERED_STATE)
4296408Sha137994 break;
4306408Sha137994
4316408Sha137994 drv_usecwait(cnex_wait_usecs);
4326408Sha137994
4336408Sha137994 } while (!panicstr && ++retries <= cnex_wait_retries);
4346408Sha137994
4356408Sha137994 return (0);
4366408Sha137994 }
4376408Sha137994
4386408Sha137994 /*
4396408Sha137994 * Returns the interrupt weight to use for the specified devclass.
4406408Sha137994 */
4416408Sha137994 static int32_t
cnex_class_weight(ldc_dev_t devclass)4426408Sha137994 cnex_class_weight(ldc_dev_t devclass)
4436408Sha137994 {
4446408Sha137994 int idx;
4456408Sha137994
4466408Sha137994 for (idx = 0; idx < CNEX_MAX_DEVS; idx++) {
4476408Sha137994 if (devclass == cnex_class_to_intr[idx].devclass) {
4486408Sha137994 return (cnex_class_to_intr[idx].weight);
4496408Sha137994 }
4506408Sha137994 }
4516408Sha137994
4526408Sha137994 /*
4536408Sha137994 * If this code is reached, the specified devclass is
4546408Sha137994 * invalid. New devclasses should be added to
4556408Sha137994 * cnex_class_to_intr.
4566408Sha137994 */
4576408Sha137994 ASSERT(0);
4586408Sha137994
4596408Sha137994 return (0);
4606408Sha137994 }
4616408Sha137994
4626408Sha137994 /*
4631991Sheppo * Exported interface to register a LDC endpoint with
4641991Sheppo * the channel nexus
4651991Sheppo */
4661991Sheppo static int
cnex_reg_chan(dev_info_t * dip,uint64_t id,ldc_dev_t devclass)4671991Sheppo cnex_reg_chan(dev_info_t *dip, uint64_t id, ldc_dev_t devclass)
4681991Sheppo {
4691991Sheppo int idx;
4701991Sheppo cnex_ldc_t *cldcp;
471*8402SMichael.Bergknoff@Sun.COM cnex_ldc_t *new_cldcp;
4721991Sheppo int listsz, num_nodes, num_channels;
4731991Sheppo md_t *mdp = NULL;
4741991Sheppo mde_cookie_t rootnode, *listp = NULL;
4753010Slm66018 uint64_t tmp_id;
4763010Slm66018 uint64_t rxino = (uint64_t)-1;
4773010Slm66018 uint64_t txino = (uint64_t)-1;
4781991Sheppo cnex_soft_state_t *cnex_ssp;
4791991Sheppo int status, instance;
4804750Sraghuram dev_info_t *chan_dip = NULL;
4811991Sheppo
4821991Sheppo /* Get device instance and structure */
4831991Sheppo instance = ddi_get_instance(dip);
4841991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
4851991Sheppo
4861991Sheppo /* Check to see if channel is already registered */
4871991Sheppo mutex_enter(&cnex_ssp->clist_lock);
4881991Sheppo cldcp = cnex_ssp->clist;
4891991Sheppo while (cldcp) {
4901991Sheppo if (cldcp->id == id) {
4911991Sheppo DWARN("cnex_reg_chan: channel 0x%llx exists\n", id);
4921991Sheppo mutex_exit(&cnex_ssp->clist_lock);
4931991Sheppo return (EINVAL);
4941991Sheppo }
4951991Sheppo cldcp = cldcp->next;
4961991Sheppo }
497*8402SMichael.Bergknoff@Sun.COM mutex_exit(&cnex_ssp->clist_lock);
4981991Sheppo
4991991Sheppo /* Get the Tx/Rx inos from the MD */
5001991Sheppo if ((mdp = md_get_handle()) == NULL) {
5011991Sheppo DWARN("cnex_reg_chan: cannot init MD\n");
5021991Sheppo return (ENXIO);
5031991Sheppo }
5041991Sheppo num_nodes = md_node_count(mdp);
5051991Sheppo ASSERT(num_nodes > 0);
5061991Sheppo
5071991Sheppo listsz = num_nodes * sizeof (mde_cookie_t);
5081991Sheppo listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP);
5091991Sheppo
5101991Sheppo rootnode = md_root_node(mdp);
5111991Sheppo
5121991Sheppo /* search for all channel_endpoint nodes */
5131991Sheppo num_channels = md_scan_dag(mdp, rootnode,
5141991Sheppo md_find_name(mdp, "channel-endpoint"),
5151991Sheppo md_find_name(mdp, "fwd"), listp);
5161991Sheppo if (num_channels <= 0) {
5171991Sheppo DWARN("cnex_reg_chan: invalid channel id\n");
5181991Sheppo kmem_free(listp, listsz);
5191991Sheppo (void) md_fini_handle(mdp);
5201991Sheppo return (EINVAL);
5211991Sheppo }
5221991Sheppo
5231991Sheppo for (idx = 0; idx < num_channels; idx++) {
5241991Sheppo
5251991Sheppo /* Get the channel ID */
5261991Sheppo status = md_get_prop_val(mdp, listp[idx], "id", &tmp_id);
5271991Sheppo if (status) {
5281991Sheppo DWARN("cnex_reg_chan: cannot read LDC ID\n");
5291991Sheppo kmem_free(listp, listsz);
5301991Sheppo (void) md_fini_handle(mdp);
5311991Sheppo return (ENXIO);
5321991Sheppo }
5331991Sheppo if (tmp_id != id)
5341991Sheppo continue;
5351991Sheppo
5361991Sheppo /* Get the Tx and Rx ino */
5371991Sheppo status = md_get_prop_val(mdp, listp[idx], "tx-ino", &txino);
5381991Sheppo if (status) {
5391991Sheppo DWARN("cnex_reg_chan: cannot read Tx ino\n");
5401991Sheppo kmem_free(listp, listsz);
5411991Sheppo (void) md_fini_handle(mdp);
5421991Sheppo return (ENXIO);
5431991Sheppo }
5441991Sheppo status = md_get_prop_val(mdp, listp[idx], "rx-ino", &rxino);
5451991Sheppo if (status) {
5461991Sheppo DWARN("cnex_reg_chan: cannot read Rx ino\n");
5471991Sheppo kmem_free(listp, listsz);
5481991Sheppo (void) md_fini_handle(mdp);
5491991Sheppo return (ENXIO);
5501991Sheppo }
5514750Sraghuram chan_dip = cnex_find_chan_dip(dip, id, mdp, listp[idx]);
5524750Sraghuram ASSERT(chan_dip != NULL);
5531991Sheppo }
5541991Sheppo kmem_free(listp, listsz);
5551991Sheppo (void) md_fini_handle(mdp);
5561991Sheppo
5573010Slm66018 /*
5583010Slm66018 * check to see if we looped through the list of channel IDs without
5593010Slm66018 * matching one (i.e. an 'ino' has not been initialised).
5603010Slm66018 */
5613010Slm66018 if ((rxino == -1) || (txino == -1)) {
5623010Slm66018 DERR("cnex_reg_chan: no ID matching '%llx' in MD\n", id);
5633010Slm66018 return (ENOENT);
5643010Slm66018 }
5653010Slm66018
5661991Sheppo /* Allocate a new channel structure */
567*8402SMichael.Bergknoff@Sun.COM new_cldcp = kmem_zalloc(sizeof (*new_cldcp), KM_SLEEP);
5681991Sheppo
5691991Sheppo /* Initialize the channel */
570*8402SMichael.Bergknoff@Sun.COM mutex_init(&new_cldcp->lock, NULL, MUTEX_DRIVER, NULL);
571*8402SMichael.Bergknoff@Sun.COM
572*8402SMichael.Bergknoff@Sun.COM new_cldcp->id = id;
573*8402SMichael.Bergknoff@Sun.COM new_cldcp->tx.ino = txino;
574*8402SMichael.Bergknoff@Sun.COM new_cldcp->rx.ino = rxino;
575*8402SMichael.Bergknoff@Sun.COM new_cldcp->devclass = devclass;
576*8402SMichael.Bergknoff@Sun.COM new_cldcp->tx.weight = CNEX_TX_INTR_WEIGHT;
577*8402SMichael.Bergknoff@Sun.COM new_cldcp->rx.weight = cnex_class_weight(devclass);
578*8402SMichael.Bergknoff@Sun.COM new_cldcp->dip = chan_dip;
5791991Sheppo
580*8402SMichael.Bergknoff@Sun.COM /*
581*8402SMichael.Bergknoff@Sun.COM * Add channel to nexus channel list.
582*8402SMichael.Bergknoff@Sun.COM * Check again to see if channel is already registered since
583*8402SMichael.Bergknoff@Sun.COM * clist_lock was dropped above.
584*8402SMichael.Bergknoff@Sun.COM */
585*8402SMichael.Bergknoff@Sun.COM mutex_enter(&cnex_ssp->clist_lock);
586*8402SMichael.Bergknoff@Sun.COM cldcp = cnex_ssp->clist;
587*8402SMichael.Bergknoff@Sun.COM while (cldcp) {
588*8402SMichael.Bergknoff@Sun.COM if (cldcp->id == id) {
589*8402SMichael.Bergknoff@Sun.COM DWARN("cnex_reg_chan: channel 0x%llx exists\n", id);
590*8402SMichael.Bergknoff@Sun.COM mutex_exit(&cnex_ssp->clist_lock);
591*8402SMichael.Bergknoff@Sun.COM mutex_destroy(&new_cldcp->lock);
592*8402SMichael.Bergknoff@Sun.COM kmem_free(new_cldcp, sizeof (*new_cldcp));
593*8402SMichael.Bergknoff@Sun.COM return (EINVAL);
594*8402SMichael.Bergknoff@Sun.COM }
595*8402SMichael.Bergknoff@Sun.COM cldcp = cldcp->next;
596*8402SMichael.Bergknoff@Sun.COM }
597*8402SMichael.Bergknoff@Sun.COM new_cldcp->next = cnex_ssp->clist;
598*8402SMichael.Bergknoff@Sun.COM cnex_ssp->clist = new_cldcp;
5991991Sheppo mutex_exit(&cnex_ssp->clist_lock);
6001991Sheppo
6011991Sheppo return (0);
6021991Sheppo }
6031991Sheppo
6041991Sheppo /*
6051991Sheppo * Add Tx/Rx interrupt handler for the channel
6061991Sheppo */
6071991Sheppo static int
cnex_add_intr(dev_info_t * dip,uint64_t id,cnex_intrtype_t itype,uint_t (* hdlr)(),caddr_t arg1,caddr_t arg2)6081991Sheppo cnex_add_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype,
6091991Sheppo uint_t (*hdlr)(), caddr_t arg1, caddr_t arg2)
6101991Sheppo {
6111991Sheppo int rv, idx, pil;
6121991Sheppo cnex_ldc_t *cldcp;
6131991Sheppo cnex_intr_t *iinfo;
6141991Sheppo cnex_soft_state_t *cnex_ssp;
6151991Sheppo int instance;
6161991Sheppo
6171991Sheppo /* Get device instance and structure */
6181991Sheppo instance = ddi_get_instance(dip);
6191991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
6201991Sheppo
6211991Sheppo /* get channel info */
6221991Sheppo mutex_enter(&cnex_ssp->clist_lock);
6231991Sheppo cldcp = cnex_ssp->clist;
6241991Sheppo while (cldcp) {
6251991Sheppo if (cldcp->id == id)
6261991Sheppo break;
6271991Sheppo cldcp = cldcp->next;
6281991Sheppo }
6291991Sheppo if (cldcp == NULL) {
6301991Sheppo DWARN("cnex_add_intr: channel 0x%llx does not exist\n", id);
6311991Sheppo mutex_exit(&cnex_ssp->clist_lock);
6321991Sheppo return (EINVAL);
6331991Sheppo }
6341991Sheppo mutex_exit(&cnex_ssp->clist_lock);
6351991Sheppo
6361991Sheppo /* get channel lock */
6371991Sheppo mutex_enter(&cldcp->lock);
6381991Sheppo
6391991Sheppo /* get interrupt type */
6401991Sheppo if (itype == CNEX_TX_INTR) {
6411991Sheppo iinfo = &(cldcp->tx);
6421991Sheppo } else if (itype == CNEX_RX_INTR) {
6431991Sheppo iinfo = &(cldcp->rx);
6441991Sheppo } else {
6451991Sheppo DWARN("cnex_add_intr: invalid interrupt type\n", id);
6461991Sheppo mutex_exit(&cldcp->lock);
6471991Sheppo return (EINVAL);
6481991Sheppo }
6491991Sheppo
6501991Sheppo /* check if a handler is already added */
6511991Sheppo if (iinfo->hdlr != 0) {
6521991Sheppo DWARN("cnex_add_intr: interrupt handler exists\n");
6531991Sheppo mutex_exit(&cldcp->lock);
6541991Sheppo return (EINVAL);
6551991Sheppo }
6561991Sheppo
6571991Sheppo /* save interrupt handler info */
6581991Sheppo iinfo->hdlr = hdlr;
6591991Sheppo iinfo->arg1 = arg1;
6601991Sheppo iinfo->arg2 = arg2;
6611991Sheppo
6625661Slm66018 /* save data for DTrace probes used by intrstat(1m) */
6635661Slm66018 iinfo->dip = cldcp->dip;
6645661Slm66018 iinfo->id = cldcp->id;
6651991Sheppo
6665513Slm66018 iinfo->icookie = MINVINTR_COOKIE + iinfo->ino;
6675513Slm66018
6681991Sheppo /*
6695513Slm66018 * Verify that the ino does not generate a cookie which
6705513Slm66018 * is outside the (MINVINTR_COOKIE, MAXIVNUM) range of the
6715513Slm66018 * system interrupt table.
6721991Sheppo */
6735513Slm66018 if (iinfo->icookie >= MAXIVNUM || iinfo->icookie < MINVINTR_COOKIE) {
6745513Slm66018 DWARN("cnex_add_intr: invalid cookie %x ino %x\n",
6755513Slm66018 iinfo->icookie, iinfo->ino);
6765513Slm66018 mutex_exit(&cldcp->lock);
6775513Slm66018 return (EINVAL);
6785513Slm66018 }
6791991Sheppo
6801991Sheppo D1("cnex_add_intr: add hdlr, cfghdl=0x%llx, ino=0x%llx, "
6811991Sheppo "cookie=0x%llx\n", cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie);
6821991Sheppo
6831991Sheppo /* Pick a PIL on the basis of the channel's devclass */
6841991Sheppo for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) {
6856408Sha137994 if (cldcp->devclass == cnex_class_to_intr[idx].devclass) {
6866408Sha137994 pil = cnex_class_to_intr[idx].pil;
6871991Sheppo break;
6881991Sheppo }
6891991Sheppo }
6901991Sheppo
6911991Sheppo /* add interrupt to solaris ivec table */
6925513Slm66018 if (add_ivintr(iinfo->icookie, pil, (intrfunc)cnex_intr_wrapper,
6935513Slm66018 (caddr_t)iinfo, NULL, NULL) != 0) {
6945513Slm66018 DWARN("cnex_add_intr: add_ivintr fail cookie %x ino %x\n",
6955513Slm66018 iinfo->icookie, iinfo->ino);
6965513Slm66018 mutex_exit(&cldcp->lock);
6975513Slm66018 return (EINVAL);
6985513Slm66018 }
6991991Sheppo
7001991Sheppo /* set the cookie in the HV */
7011991Sheppo rv = hvldc_intr_setcookie(cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie);
7021991Sheppo
7031991Sheppo /* pick next CPU in the domain for this channel */
7044750Sraghuram iinfo->cpuid = intr_dist_cpuid();
7051991Sheppo
7061991Sheppo /* set the target CPU and then enable interrupts */
7074750Sraghuram rv = hvldc_intr_settarget(cnex_ssp->cfghdl, iinfo->ino, iinfo->cpuid);
7081991Sheppo if (rv) {
7091991Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set target cpu\n",
7101991Sheppo iinfo->ino);
7111991Sheppo goto hv_error;
7121991Sheppo }
7131991Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino,
7141991Sheppo HV_INTR_IDLE_STATE);
7151991Sheppo if (rv) {
7161991Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set state\n",
7171991Sheppo iinfo->ino);
7181991Sheppo goto hv_error;
7191991Sheppo }
7201991Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, iinfo->ino, HV_INTR_VALID);
7211991Sheppo if (rv) {
7221991Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set valid\n",
7231991Sheppo iinfo->ino);
7241991Sheppo goto hv_error;
7251991Sheppo }
7261991Sheppo
7276408Sha137994 intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip,
7286408Sha137994 iinfo->weight);
7296408Sha137994
7301991Sheppo mutex_exit(&cldcp->lock);
7311991Sheppo return (0);
7321991Sheppo
7331991Sheppo hv_error:
7342973Sgovinda (void) rem_ivintr(iinfo->icookie, pil);
7351991Sheppo mutex_exit(&cldcp->lock);
7361991Sheppo return (ENXIO);
7371991Sheppo }
7381991Sheppo
7391991Sheppo
7401991Sheppo /*
7411991Sheppo * Exported interface to unregister a LDC endpoint with
7421991Sheppo * the channel nexus
7431991Sheppo */
7441991Sheppo static int
cnex_unreg_chan(dev_info_t * dip,uint64_t id)7451991Sheppo cnex_unreg_chan(dev_info_t *dip, uint64_t id)
7461991Sheppo {
7471991Sheppo cnex_ldc_t *cldcp, *prev_cldcp;
7481991Sheppo cnex_soft_state_t *cnex_ssp;
7491991Sheppo int instance;
7501991Sheppo
7511991Sheppo /* Get device instance and structure */
7521991Sheppo instance = ddi_get_instance(dip);
7531991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
7541991Sheppo
7551991Sheppo /* find and remove channel from list */
7561991Sheppo mutex_enter(&cnex_ssp->clist_lock);
7571991Sheppo prev_cldcp = NULL;
7581991Sheppo cldcp = cnex_ssp->clist;
7591991Sheppo while (cldcp) {
7601991Sheppo if (cldcp->id == id)
7611991Sheppo break;
7621991Sheppo prev_cldcp = cldcp;
7631991Sheppo cldcp = cldcp->next;
7641991Sheppo }
7651991Sheppo
7661991Sheppo if (cldcp == 0) {
7671991Sheppo DWARN("cnex_unreg_chan: invalid channel %d\n", id);
7681991Sheppo mutex_exit(&cnex_ssp->clist_lock);
7691991Sheppo return (EINVAL);
7701991Sheppo }
7711991Sheppo
7721991Sheppo if (cldcp->tx.hdlr || cldcp->rx.hdlr) {
7733010Slm66018 DWARN("cnex_unreg_chan: handlers still exist: chan %lx\n", id);
7741991Sheppo mutex_exit(&cnex_ssp->clist_lock);
7751991Sheppo return (ENXIO);
7761991Sheppo }
7771991Sheppo
7781991Sheppo if (prev_cldcp)
7791991Sheppo prev_cldcp->next = cldcp->next;
7801991Sheppo else
7811991Sheppo cnex_ssp->clist = cldcp->next;
7821991Sheppo
7831991Sheppo mutex_exit(&cnex_ssp->clist_lock);
7841991Sheppo
7851991Sheppo /* destroy mutex */
7861991Sheppo mutex_destroy(&cldcp->lock);
7871991Sheppo
7881991Sheppo /* free channel */
7891991Sheppo kmem_free(cldcp, sizeof (*cldcp));
7901991Sheppo
7911991Sheppo return (0);
7921991Sheppo }
7931991Sheppo
7941991Sheppo /*
7951991Sheppo * Remove Tx/Rx interrupt handler for the channel
7961991Sheppo */
7971991Sheppo static int
cnex_rem_intr(dev_info_t * dip,uint64_t id,cnex_intrtype_t itype)7981991Sheppo cnex_rem_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype)
7991991Sheppo {
8002973Sgovinda int rv, idx, pil;
8011991Sheppo cnex_ldc_t *cldcp;
8021991Sheppo cnex_intr_t *iinfo;
8031991Sheppo cnex_soft_state_t *cnex_ssp;
8041991Sheppo int instance, istate;
8051991Sheppo
8061991Sheppo /* Get device instance and structure */
8071991Sheppo instance = ddi_get_instance(dip);
8081991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
8091991Sheppo
8101991Sheppo /* get channel info */
8111991Sheppo mutex_enter(&cnex_ssp->clist_lock);
8121991Sheppo cldcp = cnex_ssp->clist;
8131991Sheppo while (cldcp) {
8141991Sheppo if (cldcp->id == id)
8151991Sheppo break;
8161991Sheppo cldcp = cldcp->next;
8171991Sheppo }
8181991Sheppo if (cldcp == NULL) {
8191991Sheppo DWARN("cnex_rem_intr: channel 0x%llx does not exist\n", id);
8201991Sheppo mutex_exit(&cnex_ssp->clist_lock);
8211991Sheppo return (EINVAL);
8221991Sheppo }
8231991Sheppo mutex_exit(&cnex_ssp->clist_lock);
8241991Sheppo
8251991Sheppo /* get rid of the channel intr handler */
8261991Sheppo mutex_enter(&cldcp->lock);
8271991Sheppo
8281991Sheppo /* get interrupt type */
8291991Sheppo if (itype == CNEX_TX_INTR) {
8301991Sheppo iinfo = &(cldcp->tx);
8311991Sheppo } else if (itype == CNEX_RX_INTR) {
8321991Sheppo iinfo = &(cldcp->rx);
8331991Sheppo } else {
8341991Sheppo DWARN("cnex_rem_intr: invalid interrupt type\n");
8351991Sheppo mutex_exit(&cldcp->lock);
8361991Sheppo return (EINVAL);
8371991Sheppo }
8381991Sheppo
8391991Sheppo D1("cnex_rem_intr: interrupt ino=0x%x\n", iinfo->ino);
8401991Sheppo
8411991Sheppo /* check if a handler is already added */
8421991Sheppo if (iinfo->hdlr == 0) {
8431991Sheppo DWARN("cnex_rem_intr: interrupt handler does not exist\n");
8441991Sheppo mutex_exit(&cldcp->lock);
8451991Sheppo return (EINVAL);
8461991Sheppo }
8471991Sheppo
8481991Sheppo D1("cnex_rem_intr: set intr to invalid ino=0x%x\n", iinfo->ino);
8491991Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl,
8501991Sheppo iinfo->ino, HV_INTR_NOTVALID);
8511991Sheppo if (rv) {
8521991Sheppo DWARN("cnex_rem_intr: cannot set valid ino=%x\n", iinfo->ino);
8531991Sheppo mutex_exit(&cldcp->lock);
8541991Sheppo return (ENXIO);
8551991Sheppo }
8561991Sheppo
8571991Sheppo /*
8582841Snarayan * Check if there are pending interrupts. If interrupts are
8592841Snarayan * pending return EAGAIN.
8601991Sheppo */
8612841Snarayan rv = hvldc_intr_getstate(cnex_ssp->cfghdl, iinfo->ino, &istate);
8622841Snarayan if (rv) {
8632841Snarayan DWARN("cnex_rem_intr: ino=0x%llx, cannot get state\n",
8642841Snarayan iinfo->ino);
8652841Snarayan mutex_exit(&cldcp->lock);
8662841Snarayan return (ENXIO);
8672841Snarayan }
8681991Sheppo
8691991Sheppo /* if interrupts are still pending print warning */
8701991Sheppo if (istate != HV_INTR_IDLE_STATE) {
8711991Sheppo DWARN("cnex_rem_intr: cannot remove intr busy ino=%x\n",
8721991Sheppo iinfo->ino);
8732336Snarayan mutex_exit(&cldcp->lock);
8742336Snarayan return (EAGAIN);
8751991Sheppo }
8761991Sheppo
8772973Sgovinda /* Pick a PIL on the basis of the channel's devclass */
8782973Sgovinda for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) {
8796408Sha137994 if (cldcp->devclass == cnex_class_to_intr[idx].devclass) {
8806408Sha137994 pil = cnex_class_to_intr[idx].pil;
8812973Sgovinda break;
8822973Sgovinda }
8832973Sgovinda }
8842973Sgovinda
8856408Sha137994 intr_dist_cpuid_rem_device_weight(iinfo->cpuid, iinfo->dip);
8866408Sha137994
8871991Sheppo /* remove interrupt */
8882973Sgovinda (void) rem_ivintr(iinfo->icookie, pil);
8891991Sheppo
8901991Sheppo /* clear interrupt info */
8911991Sheppo bzero(iinfo, sizeof (*iinfo));
8921991Sheppo
8931991Sheppo mutex_exit(&cldcp->lock);
8941991Sheppo
8951991Sheppo return (0);
8961991Sheppo }
8971991Sheppo
8981991Sheppo
8991991Sheppo /*
9001991Sheppo * Clear pending Tx/Rx interrupt
9011991Sheppo */
9021991Sheppo static int
cnex_clr_intr(dev_info_t * dip,uint64_t id,cnex_intrtype_t itype)9031991Sheppo cnex_clr_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype)
9041991Sheppo {
9051991Sheppo int rv;
9061991Sheppo cnex_ldc_t *cldcp;
9071991Sheppo cnex_intr_t *iinfo;
9081991Sheppo cnex_soft_state_t *cnex_ssp;
9091991Sheppo int instance;
9101991Sheppo
9111991Sheppo /* Get device instance and structure */
9121991Sheppo instance = ddi_get_instance(dip);
9131991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
9141991Sheppo
9151991Sheppo /* get channel info */
9161991Sheppo mutex_enter(&cnex_ssp->clist_lock);
9171991Sheppo cldcp = cnex_ssp->clist;
9181991Sheppo while (cldcp) {
9191991Sheppo if (cldcp->id == id)
9201991Sheppo break;
9211991Sheppo cldcp = cldcp->next;
9221991Sheppo }
9231991Sheppo if (cldcp == NULL) {
9241991Sheppo DWARN("cnex_clr_intr: channel 0x%llx does not exist\n", id);
9251991Sheppo mutex_exit(&cnex_ssp->clist_lock);
9261991Sheppo return (EINVAL);
9271991Sheppo }
9281991Sheppo mutex_exit(&cnex_ssp->clist_lock);
9291991Sheppo
9301991Sheppo mutex_enter(&cldcp->lock);
9311991Sheppo
9321991Sheppo /* get interrupt type */
9331991Sheppo if (itype == CNEX_TX_INTR) {
9341991Sheppo iinfo = &(cldcp->tx);
9351991Sheppo } else if (itype == CNEX_RX_INTR) {
9361991Sheppo iinfo = &(cldcp->rx);
9371991Sheppo } else {
9382748Slm66018 DWARN("cnex_clr_intr: invalid interrupt type\n");
9391991Sheppo mutex_exit(&cldcp->lock);
9401991Sheppo return (EINVAL);
9411991Sheppo }
9421991Sheppo
9435661Slm66018 D1("%s: interrupt ino=0x%x\n", __func__, iinfo->ino);
9441991Sheppo
9451991Sheppo /* check if a handler is already added */
9461991Sheppo if (iinfo->hdlr == 0) {
9471991Sheppo DWARN("cnex_clr_intr: interrupt handler does not exist\n");
9481991Sheppo mutex_exit(&cldcp->lock);
9491991Sheppo return (EINVAL);
9501991Sheppo }
9511991Sheppo
9521991Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino,
9531991Sheppo HV_INTR_IDLE_STATE);
9541991Sheppo if (rv) {
9552748Slm66018 DWARN("cnex_clr_intr: cannot clear interrupt state\n");
9562336Snarayan mutex_exit(&cldcp->lock);
9572336Snarayan return (ENXIO);
9581991Sheppo }
9591991Sheppo
9601991Sheppo mutex_exit(&cldcp->lock);
9611991Sheppo
9621991Sheppo return (0);
9631991Sheppo }
9641991Sheppo
9651991Sheppo /*
9661991Sheppo * Channel nexus interrupt handler wrapper
9671991Sheppo */
9681991Sheppo static uint_t
cnex_intr_wrapper(caddr_t arg)9691991Sheppo cnex_intr_wrapper(caddr_t arg)
9701991Sheppo {
9711991Sheppo int res;
9721991Sheppo uint_t (*handler)();
9731991Sheppo caddr_t handler_arg1;
9741991Sheppo caddr_t handler_arg2;
9751991Sheppo cnex_intr_t *iinfo = (cnex_intr_t *)arg;
9761991Sheppo
9771991Sheppo ASSERT(iinfo != NULL);
9781991Sheppo
9791991Sheppo handler = iinfo->hdlr;
9801991Sheppo handler_arg1 = iinfo->arg1;
9811991Sheppo handler_arg2 = iinfo->arg2;
9821991Sheppo
9834750Sraghuram /*
9844750Sraghuram * The 'interrupt__start' and 'interrupt__complete' probes
9854750Sraghuram * are provided to support 'intrstat' command. These probes
9864750Sraghuram * help monitor the interrupts on a per device basis only.
9874750Sraghuram * In order to provide the ability to monitor the
9884750Sraghuram * activity on a per channel basis, two additional
9894750Sraghuram * probes('channelintr__start','channelintr__complete')
9904750Sraghuram * are provided here.
9914750Sraghuram */
9925661Slm66018 DTRACE_PROBE4(channelintr__start, uint64_t, iinfo->id,
9934750Sraghuram cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1);
9944750Sraghuram
9955661Slm66018 DTRACE_PROBE4(interrupt__start, dev_info_t, iinfo->dip,
9964750Sraghuram void *, handler, caddr_t, handler_arg1, caddr_t, handler_arg2);
9974750Sraghuram
9984750Sraghuram D1("cnex_intr_wrapper:ino=0x%llx invoke client handler\n", iinfo->ino);
9991991Sheppo res = (*handler)(handler_arg1, handler_arg2);
10001991Sheppo
10015661Slm66018 DTRACE_PROBE4(interrupt__complete, dev_info_t, iinfo->dip,
10024750Sraghuram void *, handler, caddr_t, handler_arg1, int, res);
10034750Sraghuram
10045661Slm66018 DTRACE_PROBE4(channelintr__complete, uint64_t, iinfo->id,
10054750Sraghuram cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1);
10064750Sraghuram
10071991Sheppo return (res);
10081991Sheppo }
10091991Sheppo
10101991Sheppo /*ARGSUSED*/
10111991Sheppo static int
cnex_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)10121991Sheppo cnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
10131991Sheppo {
10141991Sheppo int rv, instance, reglen;
10151991Sheppo cnex_regspec_t *reg_p;
10161991Sheppo ldc_cnex_t cinfo;
10171991Sheppo cnex_soft_state_t *cnex_ssp;
10181991Sheppo
10191991Sheppo switch (cmd) {
10201991Sheppo case DDI_ATTACH:
10211991Sheppo break;
10221991Sheppo case DDI_RESUME:
10231991Sheppo return (DDI_SUCCESS);
10241991Sheppo default:
10251991Sheppo return (DDI_FAILURE);
10261991Sheppo }
10271991Sheppo
10281991Sheppo /*
10291991Sheppo * Get the instance specific soft state structure.
10301991Sheppo * Save the devi for this instance in the soft_state data.
10311991Sheppo */
10321991Sheppo instance = ddi_get_instance(devi);
10331991Sheppo if (ddi_soft_state_zalloc(cnex_state, instance) != DDI_SUCCESS)
10341991Sheppo return (DDI_FAILURE);
10351991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
10361991Sheppo
10371991Sheppo cnex_ssp->devi = devi;
10381991Sheppo cnex_ssp->clist = NULL;
10391991Sheppo
10401991Sheppo if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
10414750Sraghuram "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) {
10421991Sheppo return (DDI_FAILURE);
10431991Sheppo }
10441991Sheppo
10451991Sheppo /* get the sun4v config handle for this device */
10461991Sheppo cnex_ssp->cfghdl = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr);
10471991Sheppo kmem_free(reg_p, reglen);
10481991Sheppo
10491991Sheppo D1("cnex_attach: cfghdl=0x%llx\n", cnex_ssp->cfghdl);
10501991Sheppo
10511991Sheppo /* init channel list mutex */
10521991Sheppo mutex_init(&cnex_ssp->clist_lock, NULL, MUTEX_DRIVER, NULL);
10531991Sheppo
10541991Sheppo /* Register with LDC module */
10551991Sheppo cinfo.dip = devi;
10561991Sheppo cinfo.reg_chan = cnex_reg_chan;
10571991Sheppo cinfo.unreg_chan = cnex_unreg_chan;
10581991Sheppo cinfo.add_intr = cnex_add_intr;
10591991Sheppo cinfo.rem_intr = cnex_rem_intr;
10601991Sheppo cinfo.clr_intr = cnex_clr_intr;
10611991Sheppo
10621991Sheppo /*
10631991Sheppo * LDC register will fail if an nexus instance had already
10641991Sheppo * registered with the LDC framework
10651991Sheppo */
10661991Sheppo rv = ldc_register(&cinfo);
10671991Sheppo if (rv) {
10681991Sheppo DWARN("cnex_attach: unable to register with LDC\n");
10691991Sheppo ddi_soft_state_free(cnex_state, instance);
10701991Sheppo mutex_destroy(&cnex_ssp->clist_lock);
10711991Sheppo return (DDI_FAILURE);
10721991Sheppo }
10731991Sheppo
10741991Sheppo if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
10751991Sheppo DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
10761991Sheppo ddi_remove_minor_node(devi, NULL);
10771991Sheppo ddi_soft_state_free(cnex_state, instance);
10781991Sheppo mutex_destroy(&cnex_ssp->clist_lock);
10791991Sheppo return (DDI_FAILURE);
10801991Sheppo }
10811991Sheppo
10821991Sheppo /* Add interrupt redistribution callback. */
10836408Sha137994 intr_dist_add_weighted(cnex_intr_redist, cnex_ssp);
10841991Sheppo
10851991Sheppo ddi_report_dev(devi);
10861991Sheppo return (DDI_SUCCESS);
10871991Sheppo }
10881991Sheppo
10891991Sheppo /*ARGSUSED*/
10901991Sheppo static int
cnex_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)10911991Sheppo cnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
10921991Sheppo {
10931991Sheppo int instance;
10941991Sheppo ldc_cnex_t cinfo;
10951991Sheppo cnex_soft_state_t *cnex_ssp;
10961991Sheppo
10971991Sheppo switch (cmd) {
10981991Sheppo case DDI_DETACH:
10991991Sheppo break;
11001991Sheppo case DDI_SUSPEND:
11011991Sheppo return (DDI_SUCCESS);
11021991Sheppo default:
11031991Sheppo return (DDI_FAILURE);
11041991Sheppo }
11051991Sheppo
11061991Sheppo instance = ddi_get_instance(devi);
11071991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance);
11081991Sheppo
11091991Sheppo /* check if there are any channels still registered */
11101991Sheppo if (cnex_ssp->clist) {
11111991Sheppo cmn_err(CE_WARN, "?cnex_dettach: channels registered %d\n",
11121991Sheppo ddi_get_instance(devi));
11131991Sheppo return (DDI_FAILURE);
11141991Sheppo }
11151991Sheppo
11161991Sheppo /* Unregister with LDC module */
11171991Sheppo cinfo.dip = devi;
11181991Sheppo (void) ldc_unregister(&cinfo);
11191991Sheppo
11201991Sheppo /* Remove interrupt redistribution callback. */
11216408Sha137994 intr_dist_rem_weighted(cnex_intr_redist, cnex_ssp);
11221991Sheppo
11231991Sheppo /* destroy mutex */
11241991Sheppo mutex_destroy(&cnex_ssp->clist_lock);
11251991Sheppo
11261991Sheppo /* free soft state structure */
11271991Sheppo ddi_soft_state_free(cnex_state, instance);
11281991Sheppo
11291991Sheppo return (DDI_SUCCESS);
11301991Sheppo }
11311991Sheppo
11321991Sheppo /*ARGSUSED*/
11331991Sheppo static int
cnex_open(dev_t * devp,int flags,int otyp,cred_t * credp)11341991Sheppo cnex_open(dev_t *devp, int flags, int otyp, cred_t *credp)
11351991Sheppo {
11361991Sheppo int instance;
11371991Sheppo
11381991Sheppo if (otyp != OTYP_CHR)
11391991Sheppo return (EINVAL);
11401991Sheppo
11411991Sheppo instance = getminor(*devp);
11421991Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL)
11431991Sheppo return (ENXIO);
11441991Sheppo
11451991Sheppo return (0);
11461991Sheppo }
11471991Sheppo
11481991Sheppo /*ARGSUSED*/
11491991Sheppo static int
cnex_close(dev_t dev,int flags,int otyp,cred_t * credp)11501991Sheppo cnex_close(dev_t dev, int flags, int otyp, cred_t *credp)
11511991Sheppo {
11521991Sheppo int instance;
11531991Sheppo
11541991Sheppo if (otyp != OTYP_CHR)
11551991Sheppo return (EINVAL);
11561991Sheppo
11571991Sheppo instance = getminor(dev);
11581991Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL)
11591991Sheppo return (ENXIO);
11601991Sheppo
11611991Sheppo return (0);
11621991Sheppo }
11631991Sheppo
11641991Sheppo /*ARGSUSED*/
11651991Sheppo static int
cnex_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)11661991Sheppo cnex_ioctl(dev_t dev,
11671991Sheppo int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
11681991Sheppo {
11691991Sheppo int instance;
11701991Sheppo cnex_soft_state_t *cnex_ssp;
11711991Sheppo
11721991Sheppo instance = getminor(dev);
11731991Sheppo if ((cnex_ssp = ddi_get_soft_state(cnex_state, instance)) == NULL)
11741991Sheppo return (ENXIO);
11751991Sheppo ASSERT(cnex_ssp->devi);
11761991Sheppo return (ndi_devctl_ioctl(cnex_ssp->devi, cmd, arg, mode, 0));
11771991Sheppo }
11781991Sheppo
11791991Sheppo static int
cnex_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)11801991Sheppo cnex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
11811991Sheppo void *arg, void *result)
11821991Sheppo {
11831991Sheppo char name[MAXNAMELEN];
11841991Sheppo uint32_t reglen;
11851991Sheppo int *cnex_regspec;
11861991Sheppo
11871991Sheppo switch (ctlop) {
11881991Sheppo case DDI_CTLOPS_REPORTDEV:
11891991Sheppo if (rdip == NULL)
11901991Sheppo return (DDI_FAILURE);
11911991Sheppo cmn_err(CE_CONT, "?channel-device: %s%d\n",
11921991Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip));
11931991Sheppo return (DDI_SUCCESS);
11941991Sheppo
11951991Sheppo case DDI_CTLOPS_INITCHILD:
11961991Sheppo {
11971991Sheppo dev_info_t *child = (dev_info_t *)arg;
11981991Sheppo
11991991Sheppo if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
12004750Sraghuram DDI_PROP_DONTPASS, "reg",
12014750Sraghuram &cnex_regspec, ®len) != DDI_SUCCESS) {
12021991Sheppo return (DDI_FAILURE);
12031991Sheppo }
12041991Sheppo
12051991Sheppo (void) snprintf(name, sizeof (name), "%x", *cnex_regspec);
12061991Sheppo ddi_set_name_addr(child, name);
12071991Sheppo ddi_set_parent_data(child, NULL);
12081991Sheppo ddi_prop_free(cnex_regspec);
12091991Sheppo return (DDI_SUCCESS);
12101991Sheppo }
12111991Sheppo
12121991Sheppo case DDI_CTLOPS_UNINITCHILD:
12131991Sheppo {
12141991Sheppo dev_info_t *child = (dev_info_t *)arg;
12151991Sheppo
12161991Sheppo NDI_CONFIG_DEBUG((CE_NOTE,
12171991Sheppo "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)",
12181991Sheppo ddi_driver_name(child), DEVI(child)->devi_instance));
12191991Sheppo
12201991Sheppo ddi_set_name_addr(child, NULL);
12211991Sheppo
12221991Sheppo return (DDI_SUCCESS);
12231991Sheppo }
12241991Sheppo
12251991Sheppo case DDI_CTLOPS_DMAPMAPC:
12261991Sheppo case DDI_CTLOPS_REPORTINT:
12271991Sheppo case DDI_CTLOPS_REGSIZE:
12281991Sheppo case DDI_CTLOPS_NREGS:
12291991Sheppo case DDI_CTLOPS_SIDDEV:
12301991Sheppo case DDI_CTLOPS_SLAVEONLY:
12311991Sheppo case DDI_CTLOPS_AFFINITY:
12321991Sheppo case DDI_CTLOPS_POKE:
12331991Sheppo case DDI_CTLOPS_PEEK:
12341991Sheppo /*
12351991Sheppo * These ops correspond to functions that "shouldn't" be called
12361991Sheppo * by a channel-device driver. So we whine when we're called.
12371991Sheppo */
12381991Sheppo cmn_err(CE_WARN, "%s%d: invalid op (%d) from %s%d\n",
12391991Sheppo ddi_driver_name(dip), ddi_get_instance(dip), ctlop,
12401991Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip));
12411991Sheppo return (DDI_FAILURE);
12421991Sheppo
12431991Sheppo case DDI_CTLOPS_ATTACH:
12441991Sheppo case DDI_CTLOPS_BTOP:
12451991Sheppo case DDI_CTLOPS_BTOPR:
12461991Sheppo case DDI_CTLOPS_DETACH:
12471991Sheppo case DDI_CTLOPS_DVMAPAGESIZE:
12481991Sheppo case DDI_CTLOPS_IOMIN:
12491991Sheppo case DDI_CTLOPS_POWER:
12501991Sheppo case DDI_CTLOPS_PTOB:
12511991Sheppo default:
12521991Sheppo /*
12531991Sheppo * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up
12541991Sheppo */
12551991Sheppo return (ddi_ctlops(dip, rdip, ctlop, arg, result));
12561991Sheppo }
12571991Sheppo }
12581991Sheppo
12594750Sraghuram /*
12604750Sraghuram * cnex_find_chan_dip -- Find the dip of a device that is corresponding
12614750Sraghuram * to the specific channel. Below are the details on how the dip
12624750Sraghuram * is derived.
12634750Sraghuram *
12644750Sraghuram * - In the MD, the cfg-handle is expected to be unique for
12654750Sraghuram * virtual-device nodes that have the same 'name' property value.
12664750Sraghuram * This value is expected to be the same as that of "reg" property
12674750Sraghuram * of the corresponding OBP device node.
12684750Sraghuram *
12694750Sraghuram * - The value of the 'name' property of a virtual-device node
12704750Sraghuram * in the MD is expected to be the same for the corresponding
12714750Sraghuram * OBP device node.
12724750Sraghuram *
12734750Sraghuram * - Find the virtual-device node corresponding to a channel-endpoint
12744750Sraghuram * by walking backwards. Then obtain the values for the 'name' and
12754750Sraghuram * 'cfg-handle' properties.
12764750Sraghuram *
12774750Sraghuram * - Walk all the children of the cnex, find a matching dip which
12784750Sraghuram * has the same 'name' and 'reg' property values.
12794750Sraghuram *
12804750Sraghuram * - The channels that have no corresponding device driver are
12814750Sraghuram * treated as if they correspond to the cnex driver,
12824750Sraghuram * that is, return cnex dip for them. This means, the
12834750Sraghuram * cnex acts as an umbrella device driver. Note, this is
12844750Sraghuram * for 'intrstat' statistics purposes only. As a result of this,
12854750Sraghuram * the 'intrstat' shows cnex as the device that is servicing the
12864750Sraghuram * interrupts corresponding to these channels.
12874750Sraghuram *
12884750Sraghuram * For now, only one such case is known, that is, the channels that
12894750Sraghuram * are used by the "domain-services".
12904750Sraghuram */
12914750Sraghuram static dev_info_t *
cnex_find_chan_dip(dev_info_t * dip,uint64_t chan_id,md_t * mdp,mde_cookie_t mde)12924750Sraghuram cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id,
12934750Sraghuram md_t *mdp, mde_cookie_t mde)
12944750Sraghuram {
12954750Sraghuram int listsz;
12964750Sraghuram int num_nodes;
12974750Sraghuram int num_devs;
12984750Sraghuram uint64_t cfghdl;
12994750Sraghuram char *md_name;
13004750Sraghuram mde_cookie_t *listp;
13014750Sraghuram dev_info_t *cdip = NULL;
13024750Sraghuram
13034750Sraghuram num_nodes = md_node_count(mdp);
13044750Sraghuram ASSERT(num_nodes > 0);
13054750Sraghuram listsz = num_nodes * sizeof (mde_cookie_t);
13064750Sraghuram listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP);
13074750Sraghuram
13084750Sraghuram num_devs = md_scan_dag(mdp, mde, md_find_name(mdp, "virtual-device"),
13094750Sraghuram md_find_name(mdp, "back"), listp);
13104750Sraghuram ASSERT(num_devs <= 1);
13114750Sraghuram if (num_devs <= 0) {
13124750Sraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): "
13134750Sraghuram "No virtual-device found\n", chan_id);
13144750Sraghuram goto fdip_exit;
13154750Sraghuram }
13164750Sraghuram if (md_get_prop_str(mdp, listp[0], "name", &md_name) != 0) {
13174750Sraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): "
13184750Sraghuram "name property not found\n", chan_id);
13194750Sraghuram goto fdip_exit;
13204750Sraghuram }
13214750Sraghuram
13224750Sraghuram D1("cnex_find_chan_dip: channel(0x%llx): virtual-device "
13234750Sraghuram "name property value = %s\n", chan_id, md_name);
13244750Sraghuram
13254750Sraghuram if (md_get_prop_val(mdp, listp[0], "cfg-handle", &cfghdl) != 0) {
13264750Sraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): virtual-device's "
13274750Sraghuram "cfg-handle property not found\n", chan_id);
13284750Sraghuram goto fdip_exit;
13294750Sraghuram }
13304750Sraghuram
13314750Sraghuram D1("cnex_find_chan_dip:channel(0x%llx): virtual-device cfg-handle "
13324750Sraghuram " property value = 0x%x\n", chan_id, cfghdl);
13334750Sraghuram
13344750Sraghuram for (cdip = ddi_get_child(dip); cdip != NULL;
13354750Sraghuram cdip = ddi_get_next_sibling(cdip)) {
13364750Sraghuram
13374750Sraghuram int *cnex_regspec;
13384750Sraghuram uint32_t reglen;
13394750Sraghuram char *dev_name;
13404750Sraghuram
13414750Sraghuram if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip,
13424750Sraghuram DDI_PROP_DONTPASS, "name",
13434750Sraghuram &dev_name) != DDI_PROP_SUCCESS) {
13444750Sraghuram DWARN("cnex_find_chan_dip: name property not"
13454750Sraghuram " found for dip(0x%p)\n", cdip);
13464750Sraghuram continue;
13474750Sraghuram }
13484750Sraghuram if (strcmp(md_name, dev_name) != 0) {
13494750Sraghuram ddi_prop_free(dev_name);
13504750Sraghuram continue;
13514750Sraghuram }
13524750Sraghuram ddi_prop_free(dev_name);
13534750Sraghuram if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
13544750Sraghuram DDI_PROP_DONTPASS, "reg",
13554750Sraghuram &cnex_regspec, ®len) != DDI_SUCCESS) {
13564750Sraghuram DWARN("cnex_find_chan_dip: reg property not"
13574750Sraghuram " found for dip(0x%p)\n", cdip);
13584750Sraghuram continue;
13594750Sraghuram }
13604750Sraghuram if (*cnex_regspec == cfghdl) {
13614750Sraghuram D1("cnex_find_chan_dip:channel(0x%llx): found "
13624750Sraghuram "dip(0x%p) drvname=%s\n", chan_id, cdip,
13634750Sraghuram ddi_driver_name(cdip));
13645729Sarutz ddi_prop_free(cnex_regspec);
13654750Sraghuram break;
13664750Sraghuram }
13674750Sraghuram ddi_prop_free(cnex_regspec);
13684750Sraghuram }
13694750Sraghuram
13704750Sraghuram fdip_exit:
13714750Sraghuram if (cdip == NULL) {
13724750Sraghuram /*
13734750Sraghuram * If a virtual-device node exists but no dip found,
13744750Sraghuram * then for now print a DEBUG error message only.
13754750Sraghuram */
13764750Sraghuram if (num_devs > 0) {
13774750Sraghuram DERR("cnex_find_chan_dip:channel(0x%llx): "
13784750Sraghuram "No device found\n", chan_id);
13794750Sraghuram }
13804750Sraghuram
13814750Sraghuram /* If no dip was found, return cnex device's dip. */
13824750Sraghuram cdip = dip;
13834750Sraghuram }
13844750Sraghuram
13854750Sraghuram kmem_free(listp, listsz);
13864750Sraghuram D1("cnex_find_chan_dip:channel(0x%llx): returning dip=0x%p\n",
13874750Sraghuram chan_id, cdip);
13884750Sraghuram return (cdip);
13894750Sraghuram }
13904750Sraghuram
13911991Sheppo /* -------------------------------------------------------------------------- */
1392