11ae08745Sheppo /* 21ae08745Sheppo * CDDL HEADER START 31ae08745Sheppo * 41ae08745Sheppo * The contents of this file are subject to the terms of the 51ae08745Sheppo * Common Development and Distribution License (the "License"). 61ae08745Sheppo * You may not use this file except in compliance with the License. 71ae08745Sheppo * 81ae08745Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91ae08745Sheppo * or http://www.opensolaris.org/os/licensing. 101ae08745Sheppo * See the License for the specific language governing permissions 111ae08745Sheppo * and limitations under the License. 121ae08745Sheppo * 131ae08745Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141ae08745Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151ae08745Sheppo * If applicable, add the following below this CDDL HEADER, with the 161ae08745Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171ae08745Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181ae08745Sheppo * 191ae08745Sheppo * CDDL HEADER END 201ae08745Sheppo */ 211ae08745Sheppo /* 2220ae46ebSha137994 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 231ae08745Sheppo * Use is subject to license terms. 241ae08745Sheppo */ 251ae08745Sheppo 261ae08745Sheppo 271ae08745Sheppo /* 281ae08745Sheppo * Logical domain channel devices are devices implemented entirely 291ae08745Sheppo * in software; cnex is the nexus for channel-devices. They use 301ae08745Sheppo * the HV channel interfaces via the LDC transport module to send 311ae08745Sheppo * and receive data and to register callbacks. 321ae08745Sheppo */ 331ae08745Sheppo 341ae08745Sheppo #include <sys/types.h> 351ae08745Sheppo #include <sys/cmn_err.h> 361ae08745Sheppo #include <sys/conf.h> 371ae08745Sheppo #include <sys/ddi.h> 381ae08745Sheppo #include <sys/ddi_impldefs.h> 391ae08745Sheppo #include <sys/devops.h> 401ae08745Sheppo #include <sys/instance.h> 411ae08745Sheppo #include <sys/modctl.h> 421ae08745Sheppo #include <sys/open.h> 431ae08745Sheppo #include <sys/stat.h> 441ae08745Sheppo #include <sys/sunddi.h> 451ae08745Sheppo #include <sys/sunndi.h> 461ae08745Sheppo #include <sys/systm.h> 471ae08745Sheppo #include <sys/mkdev.h> 481ae08745Sheppo #include <sys/machsystm.h> 49a10abbb4Slm66018 #include <sys/intreg.h> 501ae08745Sheppo #include <sys/intr.h> 511ae08745Sheppo #include <sys/ddi_intr_impl.h> 521ae08745Sheppo #include <sys/ivintr.h> 531ae08745Sheppo #include <sys/hypervisor_api.h> 541ae08745Sheppo #include <sys/ldc.h> 551ae08745Sheppo #include <sys/cnex.h> 561ae08745Sheppo #include <sys/mach_descrip.h> 57d66f8315Sjb145095 #include <sys/hsvc.h> 580705ae3aSraghuram #include <sys/sdt.h> 591ae08745Sheppo 601ae08745Sheppo /* 611ae08745Sheppo * Internal functions/information 621ae08745Sheppo */ 6320ae46ebSha137994 static struct cnex_intr_map cnex_class_to_intr[] = { 6420ae46ebSha137994 {LDC_DEV_GENERIC, PIL_3, 0}, 6520ae46ebSha137994 {LDC_DEV_BLK, PIL_4, 10}, 6620ae46ebSha137994 {LDC_DEV_BLK_SVC, PIL_3, 10}, 6720ae46ebSha137994 {LDC_DEV_NT, PIL_6, 35}, 6820ae46ebSha137994 {LDC_DEV_NT_SVC, PIL_4, 35}, 6920ae46ebSha137994 {LDC_DEV_SERIAL, PIL_6, 0} 701ae08745Sheppo }; 7120ae46ebSha137994 #define CNEX_MAX_DEVS (sizeof (cnex_class_to_intr) / \ 7220ae46ebSha137994 sizeof (cnex_class_to_intr[0])) 7320ae46ebSha137994 7420ae46ebSha137994 #define CNEX_TX_INTR_WEIGHT 0 751ae08745Sheppo 761ae08745Sheppo #define SUN4V_REG_SPEC2CFG_HDL(x) ((x >> 32) & ~(0xfull << 28)) 771ae08745Sheppo 78a8ea4edeSnarayan static clock_t cnex_wait_usecs = 1000; /* wait time in usecs */ 790d0c8d4bSnarayan static int cnex_wait_retries = 3; 801ae08745Sheppo static void *cnex_state; 811ae08745Sheppo 821ae08745Sheppo static uint_t cnex_intr_wrapper(caddr_t arg); 830705ae3aSraghuram static dev_info_t *cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id, 840705ae3aSraghuram md_t *mdp, mde_cookie_t mde); 851ae08745Sheppo 861ae08745Sheppo /* 8720ae46ebSha137994 * Channel Interrupt Distribution 8820ae46ebSha137994 * 8920ae46ebSha137994 * In order to balance interrupts among available CPUs, we use 9020ae46ebSha137994 * the intr_dist_cpuid_{add,remove}_device_weight() interface to 9120ae46ebSha137994 * assign weights to channel interrupts. These weights, which are 9220ae46ebSha137994 * defined in the cnex_intr_map structure, influence which CPU 9320ae46ebSha137994 * is returned by intr_dist_cpuid() when called via the cnex 9420ae46ebSha137994 * interrupt redistribution callback cnex_intr_redist(). 9520ae46ebSha137994 * Interrupts for VIO devclass channels are given more weight than 9620ae46ebSha137994 * other interrupts because they are expected to occur more 9720ae46ebSha137994 * frequently and have a larger impact on overall performance. 9820ae46ebSha137994 * Transmit interrupts are given a zero weight because they are 9920ae46ebSha137994 * not used. 10020ae46ebSha137994 * 10120ae46ebSha137994 * The interrupt weights influence the target CPU selection when 10220ae46ebSha137994 * interrupts are redistributed and when they are added. However, 10320ae46ebSha137994 * removal of interrupts can unbalance the distribution even if 10420ae46ebSha137994 * they are removed in converse order--compared to the order they 10520ae46ebSha137994 * are added. This can occur when interrupts are removed after 10620ae46ebSha137994 * redistribution occurs. 10720ae46ebSha137994 * 10820ae46ebSha137994 * Channel interrupt weights affect interrupt-CPU distribution 10920ae46ebSha137994 * relative to other weighted interrupts on the system. For VIO 11020ae46ebSha137994 * devclass channels, values are chosen to match those used by 11120ae46ebSha137994 * the PCI express nexus driver for net and storage devices. 11220ae46ebSha137994 */ 11320ae46ebSha137994 static void cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight); 11420ae46ebSha137994 static int cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo); 11520ae46ebSha137994 static int cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo); 11620ae46ebSha137994 static int32_t cnex_class_weight(ldc_dev_t devclass); 11720ae46ebSha137994 11820ae46ebSha137994 /* 1191ae08745Sheppo * Debug info 1201ae08745Sheppo */ 1211ae08745Sheppo #ifdef DEBUG 1221ae08745Sheppo 1231ae08745Sheppo /* 1241ae08745Sheppo * Print debug messages 1251ae08745Sheppo * 1261ae08745Sheppo * set cnexdbg to 0xf for enabling all msgs 1271ae08745Sheppo * 0x8 - Errors 1281ae08745Sheppo * 0x4 - Warnings 1291ae08745Sheppo * 0x2 - All debug messages 1301ae08745Sheppo * 0x1 - Minimal debug messages 1311ae08745Sheppo */ 1321ae08745Sheppo 1331ae08745Sheppo int cnexdbg = 0x8; 1341ae08745Sheppo 1351ae08745Sheppo static void 1361ae08745Sheppo cnexdebug(const char *fmt, ...) 1371ae08745Sheppo { 1381ae08745Sheppo char buf[512]; 1391ae08745Sheppo va_list ap; 1401ae08745Sheppo 1411ae08745Sheppo va_start(ap, fmt); 1421ae08745Sheppo (void) vsprintf(buf, fmt, ap); 1431ae08745Sheppo va_end(ap); 1441ae08745Sheppo 1451ae08745Sheppo cmn_err(CE_CONT, "%s\n", buf); 1461ae08745Sheppo } 1471ae08745Sheppo 1481ae08745Sheppo #define D1 \ 1491ae08745Sheppo if (cnexdbg & 0x01) \ 1501ae08745Sheppo cnexdebug 1511ae08745Sheppo 1521ae08745Sheppo #define D2 \ 1531ae08745Sheppo if (cnexdbg & 0x02) \ 1541ae08745Sheppo cnexdebug 1551ae08745Sheppo 1561ae08745Sheppo #define DWARN \ 1571ae08745Sheppo if (cnexdbg & 0x04) \ 1581ae08745Sheppo cnexdebug 1591ae08745Sheppo 1601ae08745Sheppo #define DERR \ 1611ae08745Sheppo if (cnexdbg & 0x08) \ 1621ae08745Sheppo cnexdebug 1631ae08745Sheppo 1641ae08745Sheppo #else 1651ae08745Sheppo 1661ae08745Sheppo #define D1 1671ae08745Sheppo #define D2 1681ae08745Sheppo #define DWARN 1691ae08745Sheppo #define DERR 1701ae08745Sheppo 1711ae08745Sheppo #endif 1721ae08745Sheppo 1731ae08745Sheppo /* 1741ae08745Sheppo * Config information 1751ae08745Sheppo */ 1761ae08745Sheppo static int cnex_attach(dev_info_t *, ddi_attach_cmd_t); 1771ae08745Sheppo static int cnex_detach(dev_info_t *, ddi_detach_cmd_t); 1781ae08745Sheppo static int cnex_open(dev_t *, int, int, cred_t *); 1791ae08745Sheppo static int cnex_close(dev_t, int, int, cred_t *); 1801ae08745Sheppo static int cnex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 1811ae08745Sheppo static int cnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 1821ae08745Sheppo void *); 1831ae08745Sheppo 1841ae08745Sheppo static struct bus_ops cnex_bus_ops = { 1851ae08745Sheppo BUSO_REV, 1861ae08745Sheppo nullbusmap, /* bus_map */ 1871ae08745Sheppo NULL, /* bus_get_intrspec */ 1881ae08745Sheppo NULL, /* bus_add_intrspec */ 1891ae08745Sheppo NULL, /* bus_remove_intrspec */ 1901ae08745Sheppo i_ddi_map_fault, /* bus_map_fault */ 1911ae08745Sheppo ddi_no_dma_map, /* bus_dma_map */ 1921ae08745Sheppo ddi_no_dma_allochdl, /* bus_dma_allochdl */ 1931ae08745Sheppo NULL, /* bus_dma_freehdl */ 1941ae08745Sheppo NULL, /* bus_dma_bindhdl */ 1951ae08745Sheppo NULL, /* bus_dma_unbindhdl */ 1961ae08745Sheppo NULL, /* bus_dma_flush */ 1971ae08745Sheppo NULL, /* bus_dma_win */ 1981ae08745Sheppo NULL, /* bus_dma_ctl */ 1991ae08745Sheppo cnex_ctl, /* bus_ctl */ 2001ae08745Sheppo ddi_bus_prop_op, /* bus_prop_op */ 2011ae08745Sheppo 0, /* bus_get_eventcookie */ 2021ae08745Sheppo 0, /* bus_add_eventcall */ 2031ae08745Sheppo 0, /* bus_remove_eventcall */ 2041ae08745Sheppo 0, /* bus_post_event */ 2051ae08745Sheppo NULL, /* bus_intr_ctl */ 2061ae08745Sheppo NULL, /* bus_config */ 2071ae08745Sheppo NULL, /* bus_unconfig */ 2081ae08745Sheppo NULL, /* bus_fm_init */ 2091ae08745Sheppo NULL, /* bus_fm_fini */ 2101ae08745Sheppo NULL, /* bus_fm_access_enter */ 2111ae08745Sheppo NULL, /* bus_fm_access_exit */ 2121ae08745Sheppo NULL, /* bus_power */ 2131ae08745Sheppo NULL /* bus_intr_op */ 2141ae08745Sheppo }; 2151ae08745Sheppo 2161ae08745Sheppo static struct cb_ops cnex_cb_ops = { 2171ae08745Sheppo cnex_open, /* open */ 2181ae08745Sheppo cnex_close, /* close */ 2191ae08745Sheppo nodev, /* strategy */ 2201ae08745Sheppo nodev, /* print */ 2211ae08745Sheppo nodev, /* dump */ 2221ae08745Sheppo nodev, /* read */ 2231ae08745Sheppo nodev, /* write */ 2241ae08745Sheppo cnex_ioctl, /* ioctl */ 2251ae08745Sheppo nodev, /* devmap */ 2261ae08745Sheppo nodev, /* mmap */ 2271ae08745Sheppo nodev, /* segmap */ 2281ae08745Sheppo nochpoll, /* poll */ 2291ae08745Sheppo ddi_prop_op, /* cb_prop_op */ 2301ae08745Sheppo 0, /* streamtab */ 2311ae08745Sheppo D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 2321ae08745Sheppo }; 2331ae08745Sheppo 2341ae08745Sheppo static struct dev_ops cnex_ops = { 2351ae08745Sheppo DEVO_REV, /* devo_rev, */ 2361ae08745Sheppo 0, /* refcnt */ 2371ae08745Sheppo ddi_getinfo_1to1, /* info */ 2381ae08745Sheppo nulldev, /* identify */ 2391ae08745Sheppo nulldev, /* probe */ 2401ae08745Sheppo cnex_attach, /* attach */ 2411ae08745Sheppo cnex_detach, /* detach */ 2421ae08745Sheppo nodev, /* reset */ 2431ae08745Sheppo &cnex_cb_ops, /* driver operations */ 2441ae08745Sheppo &cnex_bus_ops, /* bus operations */ 24519397407SSherry Moore nulldev, /* power */ 24619397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 2471ae08745Sheppo }; 2481ae08745Sheppo 2491ae08745Sheppo /* 2501ae08745Sheppo * Module linkage information for the kernel. 2511ae08745Sheppo */ 2521ae08745Sheppo static struct modldrv modldrv = { 2531ae08745Sheppo &mod_driverops, 25419397407SSherry Moore "sun4v channel-devices nexus", 2551ae08745Sheppo &cnex_ops, 2561ae08745Sheppo }; 2571ae08745Sheppo 2581ae08745Sheppo static struct modlinkage modlinkage = { 2591ae08745Sheppo MODREV_1, (void *)&modldrv, NULL 2601ae08745Sheppo }; 2611ae08745Sheppo 2621ae08745Sheppo int 2631ae08745Sheppo _init(void) 2641ae08745Sheppo { 2651ae08745Sheppo int err; 266d66f8315Sjb145095 uint64_t majornum; 267d66f8315Sjb145095 uint64_t minornum; 268d66f8315Sjb145095 269d66f8315Sjb145095 /* 270d66f8315Sjb145095 * Check HV intr group api versioning. 271d66f8315Sjb145095 * Note that cnex assumes interrupt cookies is 272d66f8315Sjb145095 * in version 1.0 of the intr group api. 273d66f8315Sjb145095 */ 274d66f8315Sjb145095 if ((err = hsvc_version(HSVC_GROUP_INTR, &majornum, &minornum)) != 0) { 275d66f8315Sjb145095 cmn_err(CE_WARN, "cnex: failed to get intr api " 276d66f8315Sjb145095 "group versioning errno=%d", err); 277d66f8315Sjb145095 return (err); 278d66f8315Sjb145095 } else if ((majornum != 1) && (majornum != 2)) { 279d66f8315Sjb145095 cmn_err(CE_WARN, "cnex: unsupported intr api group: " 280d66f8315Sjb145095 "maj:0x%lx, min:0x%lx", majornum, minornum); 281d66f8315Sjb145095 return (ENOTSUP); 282d66f8315Sjb145095 } 2831ae08745Sheppo 2841ae08745Sheppo if ((err = ddi_soft_state_init(&cnex_state, 2851ae08745Sheppo sizeof (cnex_soft_state_t), 0)) != 0) { 2861ae08745Sheppo return (err); 2871ae08745Sheppo } 2881ae08745Sheppo if ((err = mod_install(&modlinkage)) != 0) { 2891ae08745Sheppo ddi_soft_state_fini(&cnex_state); 2901ae08745Sheppo return (err); 2911ae08745Sheppo } 2921ae08745Sheppo return (0); 2931ae08745Sheppo } 2941ae08745Sheppo 2951ae08745Sheppo int 2961ae08745Sheppo _fini(void) 2971ae08745Sheppo { 2981ae08745Sheppo int err; 2991ae08745Sheppo 3001ae08745Sheppo if ((err = mod_remove(&modlinkage)) != 0) 3011ae08745Sheppo return (err); 3021ae08745Sheppo ddi_soft_state_fini(&cnex_state); 3031ae08745Sheppo return (0); 3041ae08745Sheppo } 3051ae08745Sheppo 3061ae08745Sheppo int 3071ae08745Sheppo _info(struct modinfo *modinfop) 3081ae08745Sheppo { 3091ae08745Sheppo return (mod_info(&modlinkage, modinfop)); 3101ae08745Sheppo } 3111ae08745Sheppo 3121ae08745Sheppo /* 3131ae08745Sheppo * Callback function invoked by the interrupt redistribution 3141ae08745Sheppo * framework. This will redirect interrupts at CPUs that are 3151ae08745Sheppo * currently available in the system. 31620ae46ebSha137994 * 31720ae46ebSha137994 * Note: any interrupts with weight greater than or equal to 31820ae46ebSha137994 * weight_max must be redistributed when this callback is 31920ae46ebSha137994 * invoked with (weight == weight_max) which will be once per 32020ae46ebSha137994 * redistribution. 3211ae08745Sheppo */ 32220ae46ebSha137994 /*ARGSUSED*/ 3231ae08745Sheppo static void 32420ae46ebSha137994 cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight) 3251ae08745Sheppo { 3261ae08745Sheppo cnex_ldc_t *cldcp; 3271ae08745Sheppo cnex_soft_state_t *cnex_ssp = arg; 3281ae08745Sheppo 3291ae08745Sheppo ASSERT(cnex_ssp != NULL); 3301ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 3311ae08745Sheppo 3321ae08745Sheppo cldcp = cnex_ssp->clist; 3331ae08745Sheppo while (cldcp != NULL) { 3341ae08745Sheppo 3351ae08745Sheppo mutex_enter(&cldcp->lock); 3361ae08745Sheppo 33720ae46ebSha137994 if (cldcp->tx.hdlr && (cldcp->tx.weight == weight || 33820ae46ebSha137994 (weight_max == weight && cldcp->tx.weight > weight))) { 33920ae46ebSha137994 (void) cnex_intr_new_cpu(cnex_ssp, &cldcp->tx); 3401ae08745Sheppo } 3411ae08745Sheppo 34220ae46ebSha137994 if (cldcp->rx.hdlr && (cldcp->rx.weight == weight || 34320ae46ebSha137994 (weight_max == weight && cldcp->rx.weight > weight))) { 34420ae46ebSha137994 (void) cnex_intr_new_cpu(cnex_ssp, &cldcp->rx); 3451ae08745Sheppo } 3461ae08745Sheppo 3471ae08745Sheppo mutex_exit(&cldcp->lock); 3481ae08745Sheppo 3491ae08745Sheppo /* next channel */ 3501ae08745Sheppo cldcp = cldcp->next; 3511ae08745Sheppo } 3521ae08745Sheppo 3531ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 3541ae08745Sheppo } 3551ae08745Sheppo 3561ae08745Sheppo /* 35720ae46ebSha137994 * Internal function to replace the CPU used by an interrupt 35820ae46ebSha137994 * during interrupt redistribution. 35920ae46ebSha137994 */ 36020ae46ebSha137994 static int 36120ae46ebSha137994 cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo) 36220ae46ebSha137994 { 36320ae46ebSha137994 int intr_state; 36420ae46ebSha137994 int rv; 36520ae46ebSha137994 36620ae46ebSha137994 /* Determine if the interrupt is enabled */ 36720ae46ebSha137994 rv = hvldc_intr_getvalid(ssp->cfghdl, iinfo->ino, &intr_state); 36820ae46ebSha137994 if (rv) { 36920ae46ebSha137994 DWARN("cnex_intr_new_cpu: rx ino=0x%llx, can't get valid\n", 37020ae46ebSha137994 iinfo->ino); 37120ae46ebSha137994 return (rv); 37220ae46ebSha137994 } 37320ae46ebSha137994 37420ae46ebSha137994 /* If it is enabled, disable it */ 37520ae46ebSha137994 if (intr_state == HV_INTR_VALID) { 37620ae46ebSha137994 rv = cnex_intr_dis_wait(ssp, iinfo); 37720ae46ebSha137994 if (rv) { 37820ae46ebSha137994 return (rv); 37920ae46ebSha137994 } 38020ae46ebSha137994 } 38120ae46ebSha137994 38220ae46ebSha137994 /* Target the interrupt at a new CPU. */ 38320ae46ebSha137994 iinfo->cpuid = intr_dist_cpuid(); 38420ae46ebSha137994 (void) hvldc_intr_settarget(ssp->cfghdl, iinfo->ino, iinfo->cpuid); 38520ae46ebSha137994 intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip, 38620ae46ebSha137994 iinfo->weight); 38720ae46ebSha137994 38820ae46ebSha137994 /* Re-enable the interrupt if it was enabled */ 38920ae46ebSha137994 if (intr_state == HV_INTR_VALID) { 39020ae46ebSha137994 (void) hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino, 39120ae46ebSha137994 HV_INTR_VALID); 39220ae46ebSha137994 } 39320ae46ebSha137994 39420ae46ebSha137994 return (0); 39520ae46ebSha137994 } 39620ae46ebSha137994 39720ae46ebSha137994 /* 39820ae46ebSha137994 * Internal function to disable an interrupt and wait 39920ae46ebSha137994 * for any pending interrupts to finish. 40020ae46ebSha137994 */ 40120ae46ebSha137994 static int 40220ae46ebSha137994 cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo) 40320ae46ebSha137994 { 40420ae46ebSha137994 int rv, intr_state, retries; 40520ae46ebSha137994 40620ae46ebSha137994 /* disable interrupts */ 40720ae46ebSha137994 rv = hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino, HV_INTR_NOTVALID); 40820ae46ebSha137994 if (rv) { 40920ae46ebSha137994 DWARN("cnex_intr_dis_wait: ino=0x%llx, can't set valid\n", 41020ae46ebSha137994 iinfo->ino); 41120ae46ebSha137994 return (ENXIO); 41220ae46ebSha137994 } 41320ae46ebSha137994 41420ae46ebSha137994 /* 41520ae46ebSha137994 * Make a best effort to wait for pending interrupts 41620ae46ebSha137994 * to finish. There is not much we can do if we timeout. 41720ae46ebSha137994 */ 41820ae46ebSha137994 retries = 0; 41920ae46ebSha137994 42020ae46ebSha137994 do { 42120ae46ebSha137994 rv = hvldc_intr_getstate(ssp->cfghdl, iinfo->ino, &intr_state); 42220ae46ebSha137994 if (rv) { 42320ae46ebSha137994 DWARN("cnex_intr_dis_wait: ino=0x%llx, can't get " 42420ae46ebSha137994 "state\n", iinfo->ino); 42520ae46ebSha137994 return (ENXIO); 42620ae46ebSha137994 } 42720ae46ebSha137994 42820ae46ebSha137994 if (intr_state != HV_INTR_DELIVERED_STATE) 42920ae46ebSha137994 break; 43020ae46ebSha137994 43120ae46ebSha137994 drv_usecwait(cnex_wait_usecs); 43220ae46ebSha137994 43320ae46ebSha137994 } while (!panicstr && ++retries <= cnex_wait_retries); 43420ae46ebSha137994 43520ae46ebSha137994 return (0); 43620ae46ebSha137994 } 43720ae46ebSha137994 43820ae46ebSha137994 /* 43920ae46ebSha137994 * Returns the interrupt weight to use for the specified devclass. 44020ae46ebSha137994 */ 44120ae46ebSha137994 static int32_t 44220ae46ebSha137994 cnex_class_weight(ldc_dev_t devclass) 44320ae46ebSha137994 { 44420ae46ebSha137994 int idx; 44520ae46ebSha137994 44620ae46ebSha137994 for (idx = 0; idx < CNEX_MAX_DEVS; idx++) { 44720ae46ebSha137994 if (devclass == cnex_class_to_intr[idx].devclass) { 44820ae46ebSha137994 return (cnex_class_to_intr[idx].weight); 44920ae46ebSha137994 } 45020ae46ebSha137994 } 45120ae46ebSha137994 45220ae46ebSha137994 /* 45320ae46ebSha137994 * If this code is reached, the specified devclass is 45420ae46ebSha137994 * invalid. New devclasses should be added to 45520ae46ebSha137994 * cnex_class_to_intr. 45620ae46ebSha137994 */ 45720ae46ebSha137994 ASSERT(0); 45820ae46ebSha137994 45920ae46ebSha137994 return (0); 46020ae46ebSha137994 } 46120ae46ebSha137994 46220ae46ebSha137994 /* 4631ae08745Sheppo * Exported interface to register a LDC endpoint with 4641ae08745Sheppo * the channel nexus 4651ae08745Sheppo */ 4661ae08745Sheppo static int 4671ae08745Sheppo cnex_reg_chan(dev_info_t *dip, uint64_t id, ldc_dev_t devclass) 4681ae08745Sheppo { 4691ae08745Sheppo int idx; 4701ae08745Sheppo cnex_ldc_t *cldcp; 471*0de66c94SMichael Bergknoff cnex_ldc_t *new_cldcp; 4721ae08745Sheppo int listsz, num_nodes, num_channels; 4731ae08745Sheppo md_t *mdp = NULL; 4741ae08745Sheppo mde_cookie_t rootnode, *listp = NULL; 475cb112a14Slm66018 uint64_t tmp_id; 476cb112a14Slm66018 uint64_t rxino = (uint64_t)-1; 477cb112a14Slm66018 uint64_t txino = (uint64_t)-1; 4781ae08745Sheppo cnex_soft_state_t *cnex_ssp; 4791ae08745Sheppo int status, instance; 4800705ae3aSraghuram dev_info_t *chan_dip = NULL; 4811ae08745Sheppo 4821ae08745Sheppo /* Get device instance and structure */ 4831ae08745Sheppo instance = ddi_get_instance(dip); 4841ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 4851ae08745Sheppo 4861ae08745Sheppo /* Check to see if channel is already registered */ 4871ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 4881ae08745Sheppo cldcp = cnex_ssp->clist; 4891ae08745Sheppo while (cldcp) { 4901ae08745Sheppo if (cldcp->id == id) { 4911ae08745Sheppo DWARN("cnex_reg_chan: channel 0x%llx exists\n", id); 4921ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4931ae08745Sheppo return (EINVAL); 4941ae08745Sheppo } 4951ae08745Sheppo cldcp = cldcp->next; 4961ae08745Sheppo } 497*0de66c94SMichael Bergknoff mutex_exit(&cnex_ssp->clist_lock); 4981ae08745Sheppo 4991ae08745Sheppo /* Get the Tx/Rx inos from the MD */ 5001ae08745Sheppo if ((mdp = md_get_handle()) == NULL) { 5011ae08745Sheppo DWARN("cnex_reg_chan: cannot init MD\n"); 5021ae08745Sheppo return (ENXIO); 5031ae08745Sheppo } 5041ae08745Sheppo num_nodes = md_node_count(mdp); 5051ae08745Sheppo ASSERT(num_nodes > 0); 5061ae08745Sheppo 5071ae08745Sheppo listsz = num_nodes * sizeof (mde_cookie_t); 5081ae08745Sheppo listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP); 5091ae08745Sheppo 5101ae08745Sheppo rootnode = md_root_node(mdp); 5111ae08745Sheppo 5121ae08745Sheppo /* search for all channel_endpoint nodes */ 5131ae08745Sheppo num_channels = md_scan_dag(mdp, rootnode, 5141ae08745Sheppo md_find_name(mdp, "channel-endpoint"), 5151ae08745Sheppo md_find_name(mdp, "fwd"), listp); 5161ae08745Sheppo if (num_channels <= 0) { 5171ae08745Sheppo DWARN("cnex_reg_chan: invalid channel id\n"); 5181ae08745Sheppo kmem_free(listp, listsz); 5191ae08745Sheppo (void) md_fini_handle(mdp); 5201ae08745Sheppo return (EINVAL); 5211ae08745Sheppo } 5221ae08745Sheppo 5231ae08745Sheppo for (idx = 0; idx < num_channels; idx++) { 5241ae08745Sheppo 5251ae08745Sheppo /* Get the channel ID */ 5261ae08745Sheppo status = md_get_prop_val(mdp, listp[idx], "id", &tmp_id); 5271ae08745Sheppo if (status) { 5281ae08745Sheppo DWARN("cnex_reg_chan: cannot read LDC ID\n"); 5291ae08745Sheppo kmem_free(listp, listsz); 5301ae08745Sheppo (void) md_fini_handle(mdp); 5311ae08745Sheppo return (ENXIO); 5321ae08745Sheppo } 5331ae08745Sheppo if (tmp_id != id) 5341ae08745Sheppo continue; 5351ae08745Sheppo 5361ae08745Sheppo /* Get the Tx and Rx ino */ 5371ae08745Sheppo status = md_get_prop_val(mdp, listp[idx], "tx-ino", &txino); 5381ae08745Sheppo if (status) { 5391ae08745Sheppo DWARN("cnex_reg_chan: cannot read Tx ino\n"); 5401ae08745Sheppo kmem_free(listp, listsz); 5411ae08745Sheppo (void) md_fini_handle(mdp); 5421ae08745Sheppo return (ENXIO); 5431ae08745Sheppo } 5441ae08745Sheppo status = md_get_prop_val(mdp, listp[idx], "rx-ino", &rxino); 5451ae08745Sheppo if (status) { 5461ae08745Sheppo DWARN("cnex_reg_chan: cannot read Rx ino\n"); 5471ae08745Sheppo kmem_free(listp, listsz); 5481ae08745Sheppo (void) md_fini_handle(mdp); 5491ae08745Sheppo return (ENXIO); 5501ae08745Sheppo } 5510705ae3aSraghuram chan_dip = cnex_find_chan_dip(dip, id, mdp, listp[idx]); 5520705ae3aSraghuram ASSERT(chan_dip != NULL); 5531ae08745Sheppo } 5541ae08745Sheppo kmem_free(listp, listsz); 5551ae08745Sheppo (void) md_fini_handle(mdp); 5561ae08745Sheppo 557cb112a14Slm66018 /* 558cb112a14Slm66018 * check to see if we looped through the list of channel IDs without 559cb112a14Slm66018 * matching one (i.e. an 'ino' has not been initialised). 560cb112a14Slm66018 */ 561cb112a14Slm66018 if ((rxino == -1) || (txino == -1)) { 562cb112a14Slm66018 DERR("cnex_reg_chan: no ID matching '%llx' in MD\n", id); 563cb112a14Slm66018 return (ENOENT); 564cb112a14Slm66018 } 565cb112a14Slm66018 5661ae08745Sheppo /* Allocate a new channel structure */ 567*0de66c94SMichael Bergknoff new_cldcp = kmem_zalloc(sizeof (*new_cldcp), KM_SLEEP); 5681ae08745Sheppo 5691ae08745Sheppo /* Initialize the channel */ 570*0de66c94SMichael Bergknoff mutex_init(&new_cldcp->lock, NULL, MUTEX_DRIVER, NULL); 5711ae08745Sheppo 572*0de66c94SMichael Bergknoff new_cldcp->id = id; 573*0de66c94SMichael Bergknoff new_cldcp->tx.ino = txino; 574*0de66c94SMichael Bergknoff new_cldcp->rx.ino = rxino; 575*0de66c94SMichael Bergknoff new_cldcp->devclass = devclass; 576*0de66c94SMichael Bergknoff new_cldcp->tx.weight = CNEX_TX_INTR_WEIGHT; 577*0de66c94SMichael Bergknoff new_cldcp->rx.weight = cnex_class_weight(devclass); 578*0de66c94SMichael Bergknoff new_cldcp->dip = chan_dip; 5791ae08745Sheppo 580*0de66c94SMichael Bergknoff /* 581*0de66c94SMichael Bergknoff * Add channel to nexus channel list. 582*0de66c94SMichael Bergknoff * Check again to see if channel is already registered since 583*0de66c94SMichael Bergknoff * clist_lock was dropped above. 584*0de66c94SMichael Bergknoff */ 585*0de66c94SMichael Bergknoff mutex_enter(&cnex_ssp->clist_lock); 586*0de66c94SMichael Bergknoff cldcp = cnex_ssp->clist; 587*0de66c94SMichael Bergknoff while (cldcp) { 588*0de66c94SMichael Bergknoff if (cldcp->id == id) { 589*0de66c94SMichael Bergknoff DWARN("cnex_reg_chan: channel 0x%llx exists\n", id); 590*0de66c94SMichael Bergknoff mutex_exit(&cnex_ssp->clist_lock); 591*0de66c94SMichael Bergknoff mutex_destroy(&new_cldcp->lock); 592*0de66c94SMichael Bergknoff kmem_free(new_cldcp, sizeof (*new_cldcp)); 593*0de66c94SMichael Bergknoff return (EINVAL); 594*0de66c94SMichael Bergknoff } 595*0de66c94SMichael Bergknoff cldcp = cldcp->next; 596*0de66c94SMichael Bergknoff } 597*0de66c94SMichael Bergknoff new_cldcp->next = cnex_ssp->clist; 598*0de66c94SMichael Bergknoff cnex_ssp->clist = new_cldcp; 5991ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 6001ae08745Sheppo 6011ae08745Sheppo return (0); 6021ae08745Sheppo } 6031ae08745Sheppo 6041ae08745Sheppo /* 6051ae08745Sheppo * Add Tx/Rx interrupt handler for the channel 6061ae08745Sheppo */ 6071ae08745Sheppo static int 6081ae08745Sheppo cnex_add_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype, 6091ae08745Sheppo uint_t (*hdlr)(), caddr_t arg1, caddr_t arg2) 6101ae08745Sheppo { 6111ae08745Sheppo int rv, idx, pil; 6121ae08745Sheppo cnex_ldc_t *cldcp; 6131ae08745Sheppo cnex_intr_t *iinfo; 6141ae08745Sheppo cnex_soft_state_t *cnex_ssp; 6151ae08745Sheppo int instance; 6161ae08745Sheppo 6171ae08745Sheppo /* Get device instance and structure */ 6181ae08745Sheppo instance = ddi_get_instance(dip); 6191ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 6201ae08745Sheppo 6211ae08745Sheppo /* get channel info */ 6221ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 6231ae08745Sheppo cldcp = cnex_ssp->clist; 6241ae08745Sheppo while (cldcp) { 6251ae08745Sheppo if (cldcp->id == id) 6261ae08745Sheppo break; 6271ae08745Sheppo cldcp = cldcp->next; 6281ae08745Sheppo } 6291ae08745Sheppo if (cldcp == NULL) { 6301ae08745Sheppo DWARN("cnex_add_intr: channel 0x%llx does not exist\n", id); 6311ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 6321ae08745Sheppo return (EINVAL); 6331ae08745Sheppo } 6341ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 6351ae08745Sheppo 6361ae08745Sheppo /* get channel lock */ 6371ae08745Sheppo mutex_enter(&cldcp->lock); 6381ae08745Sheppo 6391ae08745Sheppo /* get interrupt type */ 6401ae08745Sheppo if (itype == CNEX_TX_INTR) { 6411ae08745Sheppo iinfo = &(cldcp->tx); 6421ae08745Sheppo } else if (itype == CNEX_RX_INTR) { 6431ae08745Sheppo iinfo = &(cldcp->rx); 6441ae08745Sheppo } else { 6451ae08745Sheppo DWARN("cnex_add_intr: invalid interrupt type\n", id); 6461ae08745Sheppo mutex_exit(&cldcp->lock); 6471ae08745Sheppo return (EINVAL); 6481ae08745Sheppo } 6491ae08745Sheppo 6501ae08745Sheppo /* check if a handler is already added */ 6511ae08745Sheppo if (iinfo->hdlr != 0) { 6521ae08745Sheppo DWARN("cnex_add_intr: interrupt handler exists\n"); 6531ae08745Sheppo mutex_exit(&cldcp->lock); 6541ae08745Sheppo return (EINVAL); 6551ae08745Sheppo } 6561ae08745Sheppo 6571ae08745Sheppo /* save interrupt handler info */ 6581ae08745Sheppo iinfo->hdlr = hdlr; 6591ae08745Sheppo iinfo->arg1 = arg1; 6601ae08745Sheppo iinfo->arg2 = arg2; 6611ae08745Sheppo 662928da554Slm66018 /* save data for DTrace probes used by intrstat(1m) */ 663928da554Slm66018 iinfo->dip = cldcp->dip; 664928da554Slm66018 iinfo->id = cldcp->id; 6651ae08745Sheppo 666a10abbb4Slm66018 iinfo->icookie = MINVINTR_COOKIE + iinfo->ino; 667a10abbb4Slm66018 6681ae08745Sheppo /* 669a10abbb4Slm66018 * Verify that the ino does not generate a cookie which 670a10abbb4Slm66018 * is outside the (MINVINTR_COOKIE, MAXIVNUM) range of the 671a10abbb4Slm66018 * system interrupt table. 6721ae08745Sheppo */ 673a10abbb4Slm66018 if (iinfo->icookie >= MAXIVNUM || iinfo->icookie < MINVINTR_COOKIE) { 674a10abbb4Slm66018 DWARN("cnex_add_intr: invalid cookie %x ino %x\n", 675a10abbb4Slm66018 iinfo->icookie, iinfo->ino); 676a10abbb4Slm66018 mutex_exit(&cldcp->lock); 677a10abbb4Slm66018 return (EINVAL); 678a10abbb4Slm66018 } 6791ae08745Sheppo 6801ae08745Sheppo D1("cnex_add_intr: add hdlr, cfghdl=0x%llx, ino=0x%llx, " 6811ae08745Sheppo "cookie=0x%llx\n", cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie); 6821ae08745Sheppo 6831ae08745Sheppo /* Pick a PIL on the basis of the channel's devclass */ 6841ae08745Sheppo for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) { 68520ae46ebSha137994 if (cldcp->devclass == cnex_class_to_intr[idx].devclass) { 68620ae46ebSha137994 pil = cnex_class_to_intr[idx].pil; 6871ae08745Sheppo break; 6881ae08745Sheppo } 6891ae08745Sheppo } 6901ae08745Sheppo 6911ae08745Sheppo /* add interrupt to solaris ivec table */ 692a10abbb4Slm66018 if (add_ivintr(iinfo->icookie, pil, (intrfunc)cnex_intr_wrapper, 693a10abbb4Slm66018 (caddr_t)iinfo, NULL, NULL) != 0) { 694a10abbb4Slm66018 DWARN("cnex_add_intr: add_ivintr fail cookie %x ino %x\n", 695a10abbb4Slm66018 iinfo->icookie, iinfo->ino); 696a10abbb4Slm66018 mutex_exit(&cldcp->lock); 697a10abbb4Slm66018 return (EINVAL); 698a10abbb4Slm66018 } 6991ae08745Sheppo 7001ae08745Sheppo /* set the cookie in the HV */ 7011ae08745Sheppo rv = hvldc_intr_setcookie(cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie); 7021ae08745Sheppo 7031ae08745Sheppo /* pick next CPU in the domain for this channel */ 7040705ae3aSraghuram iinfo->cpuid = intr_dist_cpuid(); 7051ae08745Sheppo 7061ae08745Sheppo /* set the target CPU and then enable interrupts */ 7070705ae3aSraghuram rv = hvldc_intr_settarget(cnex_ssp->cfghdl, iinfo->ino, iinfo->cpuid); 7081ae08745Sheppo if (rv) { 7091ae08745Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set target cpu\n", 7101ae08745Sheppo iinfo->ino); 7111ae08745Sheppo goto hv_error; 7121ae08745Sheppo } 7131ae08745Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino, 7141ae08745Sheppo HV_INTR_IDLE_STATE); 7151ae08745Sheppo if (rv) { 7161ae08745Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set state\n", 7171ae08745Sheppo iinfo->ino); 7181ae08745Sheppo goto hv_error; 7191ae08745Sheppo } 7201ae08745Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, iinfo->ino, HV_INTR_VALID); 7211ae08745Sheppo if (rv) { 7221ae08745Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set valid\n", 7231ae08745Sheppo iinfo->ino); 7241ae08745Sheppo goto hv_error; 7251ae08745Sheppo } 7261ae08745Sheppo 72720ae46ebSha137994 intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip, 72820ae46ebSha137994 iinfo->weight); 72920ae46ebSha137994 7301ae08745Sheppo mutex_exit(&cldcp->lock); 7311ae08745Sheppo return (0); 7321ae08745Sheppo 7331ae08745Sheppo hv_error: 734b0fc0e77Sgovinda (void) rem_ivintr(iinfo->icookie, pil); 7351ae08745Sheppo mutex_exit(&cldcp->lock); 7361ae08745Sheppo return (ENXIO); 7371ae08745Sheppo } 7381ae08745Sheppo 7391ae08745Sheppo 7401ae08745Sheppo /* 7411ae08745Sheppo * Exported interface to unregister a LDC endpoint with 7421ae08745Sheppo * the channel nexus 7431ae08745Sheppo */ 7441ae08745Sheppo static int 7451ae08745Sheppo cnex_unreg_chan(dev_info_t *dip, uint64_t id) 7461ae08745Sheppo { 7471ae08745Sheppo cnex_ldc_t *cldcp, *prev_cldcp; 7481ae08745Sheppo cnex_soft_state_t *cnex_ssp; 7491ae08745Sheppo int instance; 7501ae08745Sheppo 7511ae08745Sheppo /* Get device instance and structure */ 7521ae08745Sheppo instance = ddi_get_instance(dip); 7531ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 7541ae08745Sheppo 7551ae08745Sheppo /* find and remove channel from list */ 7561ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 7571ae08745Sheppo prev_cldcp = NULL; 7581ae08745Sheppo cldcp = cnex_ssp->clist; 7591ae08745Sheppo while (cldcp) { 7601ae08745Sheppo if (cldcp->id == id) 7611ae08745Sheppo break; 7621ae08745Sheppo prev_cldcp = cldcp; 7631ae08745Sheppo cldcp = cldcp->next; 7641ae08745Sheppo } 7651ae08745Sheppo 7661ae08745Sheppo if (cldcp == 0) { 7671ae08745Sheppo DWARN("cnex_unreg_chan: invalid channel %d\n", id); 7681ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7691ae08745Sheppo return (EINVAL); 7701ae08745Sheppo } 7711ae08745Sheppo 7721ae08745Sheppo if (cldcp->tx.hdlr || cldcp->rx.hdlr) { 773cb112a14Slm66018 DWARN("cnex_unreg_chan: handlers still exist: chan %lx\n", id); 7741ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7751ae08745Sheppo return (ENXIO); 7761ae08745Sheppo } 7771ae08745Sheppo 7781ae08745Sheppo if (prev_cldcp) 7791ae08745Sheppo prev_cldcp->next = cldcp->next; 7801ae08745Sheppo else 7811ae08745Sheppo cnex_ssp->clist = cldcp->next; 7821ae08745Sheppo 7831ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7841ae08745Sheppo 7851ae08745Sheppo /* destroy mutex */ 7861ae08745Sheppo mutex_destroy(&cldcp->lock); 7871ae08745Sheppo 7881ae08745Sheppo /* free channel */ 7891ae08745Sheppo kmem_free(cldcp, sizeof (*cldcp)); 7901ae08745Sheppo 7911ae08745Sheppo return (0); 7921ae08745Sheppo } 7931ae08745Sheppo 7941ae08745Sheppo /* 7951ae08745Sheppo * Remove Tx/Rx interrupt handler for the channel 7961ae08745Sheppo */ 7971ae08745Sheppo static int 7981ae08745Sheppo cnex_rem_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype) 7991ae08745Sheppo { 800b0fc0e77Sgovinda int rv, idx, pil; 8011ae08745Sheppo cnex_ldc_t *cldcp; 8021ae08745Sheppo cnex_intr_t *iinfo; 8031ae08745Sheppo cnex_soft_state_t *cnex_ssp; 8041ae08745Sheppo int instance, istate; 8051ae08745Sheppo 8061ae08745Sheppo /* Get device instance and structure */ 8071ae08745Sheppo instance = ddi_get_instance(dip); 8081ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 8091ae08745Sheppo 8101ae08745Sheppo /* get channel info */ 8111ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 8121ae08745Sheppo cldcp = cnex_ssp->clist; 8131ae08745Sheppo while (cldcp) { 8141ae08745Sheppo if (cldcp->id == id) 8151ae08745Sheppo break; 8161ae08745Sheppo cldcp = cldcp->next; 8171ae08745Sheppo } 8181ae08745Sheppo if (cldcp == NULL) { 8191ae08745Sheppo DWARN("cnex_rem_intr: channel 0x%llx does not exist\n", id); 8201ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 8211ae08745Sheppo return (EINVAL); 8221ae08745Sheppo } 8231ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 8241ae08745Sheppo 8251ae08745Sheppo /* get rid of the channel intr handler */ 8261ae08745Sheppo mutex_enter(&cldcp->lock); 8271ae08745Sheppo 8281ae08745Sheppo /* get interrupt type */ 8291ae08745Sheppo if (itype == CNEX_TX_INTR) { 8301ae08745Sheppo iinfo = &(cldcp->tx); 8311ae08745Sheppo } else if (itype == CNEX_RX_INTR) { 8321ae08745Sheppo iinfo = &(cldcp->rx); 8331ae08745Sheppo } else { 8341ae08745Sheppo DWARN("cnex_rem_intr: invalid interrupt type\n"); 8351ae08745Sheppo mutex_exit(&cldcp->lock); 8361ae08745Sheppo return (EINVAL); 8371ae08745Sheppo } 8381ae08745Sheppo 8391ae08745Sheppo D1("cnex_rem_intr: interrupt ino=0x%x\n", iinfo->ino); 8401ae08745Sheppo 8411ae08745Sheppo /* check if a handler is already added */ 8421ae08745Sheppo if (iinfo->hdlr == 0) { 8431ae08745Sheppo DWARN("cnex_rem_intr: interrupt handler does not exist\n"); 8441ae08745Sheppo mutex_exit(&cldcp->lock); 8451ae08745Sheppo return (EINVAL); 8461ae08745Sheppo } 8471ae08745Sheppo 8481ae08745Sheppo D1("cnex_rem_intr: set intr to invalid ino=0x%x\n", iinfo->ino); 8491ae08745Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, 8501ae08745Sheppo iinfo->ino, HV_INTR_NOTVALID); 8511ae08745Sheppo if (rv) { 8521ae08745Sheppo DWARN("cnex_rem_intr: cannot set valid ino=%x\n", iinfo->ino); 8531ae08745Sheppo mutex_exit(&cldcp->lock); 8541ae08745Sheppo return (ENXIO); 8551ae08745Sheppo } 8561ae08745Sheppo 8571ae08745Sheppo /* 858a8ea4edeSnarayan * Check if there are pending interrupts. If interrupts are 859a8ea4edeSnarayan * pending return EAGAIN. 8601ae08745Sheppo */ 8611ae08745Sheppo rv = hvldc_intr_getstate(cnex_ssp->cfghdl, iinfo->ino, &istate); 8621ae08745Sheppo if (rv) { 8631ae08745Sheppo DWARN("cnex_rem_intr: ino=0x%llx, cannot get state\n", 8641ae08745Sheppo iinfo->ino); 865d10e4ef2Snarayan mutex_exit(&cldcp->lock); 866d10e4ef2Snarayan return (ENXIO); 8671ae08745Sheppo } 8681ae08745Sheppo 8691ae08745Sheppo /* if interrupts are still pending print warning */ 8701ae08745Sheppo if (istate != HV_INTR_IDLE_STATE) { 8711ae08745Sheppo DWARN("cnex_rem_intr: cannot remove intr busy ino=%x\n", 8721ae08745Sheppo iinfo->ino); 873d10e4ef2Snarayan mutex_exit(&cldcp->lock); 874d10e4ef2Snarayan return (EAGAIN); 8751ae08745Sheppo } 8761ae08745Sheppo 877b0fc0e77Sgovinda /* Pick a PIL on the basis of the channel's devclass */ 878b0fc0e77Sgovinda for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) { 87920ae46ebSha137994 if (cldcp->devclass == cnex_class_to_intr[idx].devclass) { 88020ae46ebSha137994 pil = cnex_class_to_intr[idx].pil; 881b0fc0e77Sgovinda break; 882b0fc0e77Sgovinda } 883b0fc0e77Sgovinda } 884b0fc0e77Sgovinda 88520ae46ebSha137994 intr_dist_cpuid_rem_device_weight(iinfo->cpuid, iinfo->dip); 88620ae46ebSha137994 8871ae08745Sheppo /* remove interrupt */ 888b0fc0e77Sgovinda (void) rem_ivintr(iinfo->icookie, pil); 8891ae08745Sheppo 8901ae08745Sheppo /* clear interrupt info */ 8911ae08745Sheppo bzero(iinfo, sizeof (*iinfo)); 8921ae08745Sheppo 8931ae08745Sheppo mutex_exit(&cldcp->lock); 8941ae08745Sheppo 8951ae08745Sheppo return (0); 8961ae08745Sheppo } 8971ae08745Sheppo 8981ae08745Sheppo 8991ae08745Sheppo /* 9001ae08745Sheppo * Clear pending Tx/Rx interrupt 9011ae08745Sheppo */ 9021ae08745Sheppo static int 9031ae08745Sheppo cnex_clr_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype) 9041ae08745Sheppo { 9051ae08745Sheppo int rv; 9061ae08745Sheppo cnex_ldc_t *cldcp; 9071ae08745Sheppo cnex_intr_t *iinfo; 9081ae08745Sheppo cnex_soft_state_t *cnex_ssp; 9091ae08745Sheppo int instance; 9101ae08745Sheppo 9111ae08745Sheppo /* Get device instance and structure */ 9121ae08745Sheppo instance = ddi_get_instance(dip); 9131ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 9141ae08745Sheppo 9151ae08745Sheppo /* get channel info */ 9161ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 9171ae08745Sheppo cldcp = cnex_ssp->clist; 9181ae08745Sheppo while (cldcp) { 9191ae08745Sheppo if (cldcp->id == id) 9201ae08745Sheppo break; 9211ae08745Sheppo cldcp = cldcp->next; 9221ae08745Sheppo } 9231ae08745Sheppo if (cldcp == NULL) { 9241ae08745Sheppo DWARN("cnex_clr_intr: channel 0x%llx does not exist\n", id); 9251ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 9261ae08745Sheppo return (EINVAL); 9271ae08745Sheppo } 9281ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 9291ae08745Sheppo 9301ae08745Sheppo mutex_enter(&cldcp->lock); 9311ae08745Sheppo 9321ae08745Sheppo /* get interrupt type */ 9331ae08745Sheppo if (itype == CNEX_TX_INTR) { 9341ae08745Sheppo iinfo = &(cldcp->tx); 9351ae08745Sheppo } else if (itype == CNEX_RX_INTR) { 9361ae08745Sheppo iinfo = &(cldcp->rx); 9371ae08745Sheppo } else { 9387636cb21Slm66018 DWARN("cnex_clr_intr: invalid interrupt type\n"); 9391ae08745Sheppo mutex_exit(&cldcp->lock); 9401ae08745Sheppo return (EINVAL); 9411ae08745Sheppo } 9421ae08745Sheppo 943928da554Slm66018 D1("%s: interrupt ino=0x%x\n", __func__, iinfo->ino); 9441ae08745Sheppo 9451ae08745Sheppo /* check if a handler is already added */ 9461ae08745Sheppo if (iinfo->hdlr == 0) { 9471ae08745Sheppo DWARN("cnex_clr_intr: interrupt handler does not exist\n"); 9481ae08745Sheppo mutex_exit(&cldcp->lock); 9491ae08745Sheppo return (EINVAL); 9501ae08745Sheppo } 9511ae08745Sheppo 9521ae08745Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino, 9531ae08745Sheppo HV_INTR_IDLE_STATE); 9541ae08745Sheppo if (rv) { 9557636cb21Slm66018 DWARN("cnex_clr_intr: cannot clear interrupt state\n"); 956d10e4ef2Snarayan mutex_exit(&cldcp->lock); 957d10e4ef2Snarayan return (ENXIO); 9581ae08745Sheppo } 9591ae08745Sheppo 9601ae08745Sheppo mutex_exit(&cldcp->lock); 9611ae08745Sheppo 9621ae08745Sheppo return (0); 9631ae08745Sheppo } 9641ae08745Sheppo 9651ae08745Sheppo /* 9661ae08745Sheppo * Channel nexus interrupt handler wrapper 9671ae08745Sheppo */ 9681ae08745Sheppo static uint_t 9691ae08745Sheppo cnex_intr_wrapper(caddr_t arg) 9701ae08745Sheppo { 9711ae08745Sheppo int res; 9721ae08745Sheppo uint_t (*handler)(); 9731ae08745Sheppo caddr_t handler_arg1; 9741ae08745Sheppo caddr_t handler_arg2; 9751ae08745Sheppo cnex_intr_t *iinfo = (cnex_intr_t *)arg; 9761ae08745Sheppo 9771ae08745Sheppo ASSERT(iinfo != NULL); 9781ae08745Sheppo 9791ae08745Sheppo handler = iinfo->hdlr; 9801ae08745Sheppo handler_arg1 = iinfo->arg1; 9811ae08745Sheppo handler_arg2 = iinfo->arg2; 9821ae08745Sheppo 9830705ae3aSraghuram /* 9840705ae3aSraghuram * The 'interrupt__start' and 'interrupt__complete' probes 9850705ae3aSraghuram * are provided to support 'intrstat' command. These probes 9860705ae3aSraghuram * help monitor the interrupts on a per device basis only. 9870705ae3aSraghuram * In order to provide the ability to monitor the 9880705ae3aSraghuram * activity on a per channel basis, two additional 9890705ae3aSraghuram * probes('channelintr__start','channelintr__complete') 9900705ae3aSraghuram * are provided here. 9910705ae3aSraghuram */ 992928da554Slm66018 DTRACE_PROBE4(channelintr__start, uint64_t, iinfo->id, 9930705ae3aSraghuram cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1); 9940705ae3aSraghuram 995928da554Slm66018 DTRACE_PROBE4(interrupt__start, dev_info_t, iinfo->dip, 9960705ae3aSraghuram void *, handler, caddr_t, handler_arg1, caddr_t, handler_arg2); 9970705ae3aSraghuram 9981ae08745Sheppo D1("cnex_intr_wrapper:ino=0x%llx invoke client handler\n", iinfo->ino); 9991ae08745Sheppo res = (*handler)(handler_arg1, handler_arg2); 10001ae08745Sheppo 1001928da554Slm66018 DTRACE_PROBE4(interrupt__complete, dev_info_t, iinfo->dip, 10020705ae3aSraghuram void *, handler, caddr_t, handler_arg1, int, res); 10030705ae3aSraghuram 1004928da554Slm66018 DTRACE_PROBE4(channelintr__complete, uint64_t, iinfo->id, 10050705ae3aSraghuram cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1); 10060705ae3aSraghuram 10071ae08745Sheppo return (res); 10081ae08745Sheppo } 10091ae08745Sheppo 10101ae08745Sheppo /*ARGSUSED*/ 10111ae08745Sheppo static int 10121ae08745Sheppo cnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 10131ae08745Sheppo { 10141ae08745Sheppo int rv, instance, reglen; 10151ae08745Sheppo cnex_regspec_t *reg_p; 10161ae08745Sheppo ldc_cnex_t cinfo; 10171ae08745Sheppo cnex_soft_state_t *cnex_ssp; 10181ae08745Sheppo 10191ae08745Sheppo switch (cmd) { 10201ae08745Sheppo case DDI_ATTACH: 10211ae08745Sheppo break; 10221ae08745Sheppo case DDI_RESUME: 10231ae08745Sheppo return (DDI_SUCCESS); 10241ae08745Sheppo default: 10251ae08745Sheppo return (DDI_FAILURE); 10261ae08745Sheppo } 10271ae08745Sheppo 10281ae08745Sheppo /* 10291ae08745Sheppo * Get the instance specific soft state structure. 10301ae08745Sheppo * Save the devi for this instance in the soft_state data. 10311ae08745Sheppo */ 10321ae08745Sheppo instance = ddi_get_instance(devi); 10331ae08745Sheppo if (ddi_soft_state_zalloc(cnex_state, instance) != DDI_SUCCESS) 10341ae08745Sheppo return (DDI_FAILURE); 10351ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 10361ae08745Sheppo 10371ae08745Sheppo cnex_ssp->devi = devi; 10381ae08745Sheppo cnex_ssp->clist = NULL; 10391ae08745Sheppo 10401ae08745Sheppo if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 10411ae08745Sheppo "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) { 10421ae08745Sheppo return (DDI_FAILURE); 10431ae08745Sheppo } 10441ae08745Sheppo 10451ae08745Sheppo /* get the sun4v config handle for this device */ 10461ae08745Sheppo cnex_ssp->cfghdl = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr); 10471ae08745Sheppo kmem_free(reg_p, reglen); 10481ae08745Sheppo 10491ae08745Sheppo D1("cnex_attach: cfghdl=0x%llx\n", cnex_ssp->cfghdl); 10501ae08745Sheppo 10511ae08745Sheppo /* init channel list mutex */ 10521ae08745Sheppo mutex_init(&cnex_ssp->clist_lock, NULL, MUTEX_DRIVER, NULL); 10531ae08745Sheppo 10541ae08745Sheppo /* Register with LDC module */ 10551ae08745Sheppo cinfo.dip = devi; 10561ae08745Sheppo cinfo.reg_chan = cnex_reg_chan; 10571ae08745Sheppo cinfo.unreg_chan = cnex_unreg_chan; 10581ae08745Sheppo cinfo.add_intr = cnex_add_intr; 10591ae08745Sheppo cinfo.rem_intr = cnex_rem_intr; 10601ae08745Sheppo cinfo.clr_intr = cnex_clr_intr; 10611ae08745Sheppo 10621ae08745Sheppo /* 10631ae08745Sheppo * LDC register will fail if an nexus instance had already 10641ae08745Sheppo * registered with the LDC framework 10651ae08745Sheppo */ 10661ae08745Sheppo rv = ldc_register(&cinfo); 10671ae08745Sheppo if (rv) { 10681ae08745Sheppo DWARN("cnex_attach: unable to register with LDC\n"); 10691ae08745Sheppo ddi_soft_state_free(cnex_state, instance); 10701ae08745Sheppo mutex_destroy(&cnex_ssp->clist_lock); 10711ae08745Sheppo return (DDI_FAILURE); 10721ae08745Sheppo } 10731ae08745Sheppo 10741ae08745Sheppo if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance, 10751ae08745Sheppo DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 10761ae08745Sheppo ddi_remove_minor_node(devi, NULL); 10771ae08745Sheppo ddi_soft_state_free(cnex_state, instance); 10781ae08745Sheppo mutex_destroy(&cnex_ssp->clist_lock); 10791ae08745Sheppo return (DDI_FAILURE); 10801ae08745Sheppo } 10811ae08745Sheppo 10821ae08745Sheppo /* Add interrupt redistribution callback. */ 108320ae46ebSha137994 intr_dist_add_weighted(cnex_intr_redist, cnex_ssp); 10841ae08745Sheppo 10851ae08745Sheppo ddi_report_dev(devi); 10861ae08745Sheppo return (DDI_SUCCESS); 10871ae08745Sheppo } 10881ae08745Sheppo 10891ae08745Sheppo /*ARGSUSED*/ 10901ae08745Sheppo static int 10911ae08745Sheppo cnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 10921ae08745Sheppo { 10931ae08745Sheppo int instance; 10941ae08745Sheppo ldc_cnex_t cinfo; 10951ae08745Sheppo cnex_soft_state_t *cnex_ssp; 10961ae08745Sheppo 10971ae08745Sheppo switch (cmd) { 10981ae08745Sheppo case DDI_DETACH: 10991ae08745Sheppo break; 11001ae08745Sheppo case DDI_SUSPEND: 11011ae08745Sheppo return (DDI_SUCCESS); 11021ae08745Sheppo default: 11031ae08745Sheppo return (DDI_FAILURE); 11041ae08745Sheppo } 11051ae08745Sheppo 11061ae08745Sheppo instance = ddi_get_instance(devi); 11071ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 11081ae08745Sheppo 11091ae08745Sheppo /* check if there are any channels still registered */ 11101ae08745Sheppo if (cnex_ssp->clist) { 11111ae08745Sheppo cmn_err(CE_WARN, "?cnex_dettach: channels registered %d\n", 11121ae08745Sheppo ddi_get_instance(devi)); 11131ae08745Sheppo return (DDI_FAILURE); 11141ae08745Sheppo } 11151ae08745Sheppo 11161ae08745Sheppo /* Unregister with LDC module */ 11171ae08745Sheppo cinfo.dip = devi; 11181ae08745Sheppo (void) ldc_unregister(&cinfo); 11191ae08745Sheppo 11201ae08745Sheppo /* Remove interrupt redistribution callback. */ 112120ae46ebSha137994 intr_dist_rem_weighted(cnex_intr_redist, cnex_ssp); 11221ae08745Sheppo 11231ae08745Sheppo /* destroy mutex */ 11241ae08745Sheppo mutex_destroy(&cnex_ssp->clist_lock); 11251ae08745Sheppo 11261ae08745Sheppo /* free soft state structure */ 11271ae08745Sheppo ddi_soft_state_free(cnex_state, instance); 11281ae08745Sheppo 11291ae08745Sheppo return (DDI_SUCCESS); 11301ae08745Sheppo } 11311ae08745Sheppo 11321ae08745Sheppo /*ARGSUSED*/ 11331ae08745Sheppo static int 11341ae08745Sheppo cnex_open(dev_t *devp, int flags, int otyp, cred_t *credp) 11351ae08745Sheppo { 11361ae08745Sheppo int instance; 11371ae08745Sheppo 11381ae08745Sheppo if (otyp != OTYP_CHR) 11391ae08745Sheppo return (EINVAL); 11401ae08745Sheppo 11411ae08745Sheppo instance = getminor(*devp); 11421ae08745Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL) 11431ae08745Sheppo return (ENXIO); 11441ae08745Sheppo 11451ae08745Sheppo return (0); 11461ae08745Sheppo } 11471ae08745Sheppo 11481ae08745Sheppo /*ARGSUSED*/ 11491ae08745Sheppo static int 11501ae08745Sheppo cnex_close(dev_t dev, int flags, int otyp, cred_t *credp) 11511ae08745Sheppo { 11521ae08745Sheppo int instance; 11531ae08745Sheppo 11541ae08745Sheppo if (otyp != OTYP_CHR) 11551ae08745Sheppo return (EINVAL); 11561ae08745Sheppo 11571ae08745Sheppo instance = getminor(dev); 11581ae08745Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL) 11591ae08745Sheppo return (ENXIO); 11601ae08745Sheppo 11611ae08745Sheppo return (0); 11621ae08745Sheppo } 11631ae08745Sheppo 11641ae08745Sheppo /*ARGSUSED*/ 11651ae08745Sheppo static int 11661ae08745Sheppo cnex_ioctl(dev_t dev, 11671ae08745Sheppo int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 11681ae08745Sheppo { 11691ae08745Sheppo int instance; 11701ae08745Sheppo cnex_soft_state_t *cnex_ssp; 11711ae08745Sheppo 11721ae08745Sheppo instance = getminor(dev); 11731ae08745Sheppo if ((cnex_ssp = ddi_get_soft_state(cnex_state, instance)) == NULL) 11741ae08745Sheppo return (ENXIO); 11751ae08745Sheppo ASSERT(cnex_ssp->devi); 11761ae08745Sheppo return (ndi_devctl_ioctl(cnex_ssp->devi, cmd, arg, mode, 0)); 11771ae08745Sheppo } 11781ae08745Sheppo 11791ae08745Sheppo static int 11801ae08745Sheppo cnex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 11811ae08745Sheppo void *arg, void *result) 11821ae08745Sheppo { 11831ae08745Sheppo char name[MAXNAMELEN]; 11841ae08745Sheppo uint32_t reglen; 11851ae08745Sheppo int *cnex_regspec; 11861ae08745Sheppo 11871ae08745Sheppo switch (ctlop) { 11881ae08745Sheppo case DDI_CTLOPS_REPORTDEV: 11891ae08745Sheppo if (rdip == NULL) 11901ae08745Sheppo return (DDI_FAILURE); 11911ae08745Sheppo cmn_err(CE_CONT, "?channel-device: %s%d\n", 11921ae08745Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip)); 11931ae08745Sheppo return (DDI_SUCCESS); 11941ae08745Sheppo 11951ae08745Sheppo case DDI_CTLOPS_INITCHILD: 11961ae08745Sheppo { 11971ae08745Sheppo dev_info_t *child = (dev_info_t *)arg; 11981ae08745Sheppo 11991ae08745Sheppo if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 12001ae08745Sheppo DDI_PROP_DONTPASS, "reg", 12011ae08745Sheppo &cnex_regspec, ®len) != DDI_SUCCESS) { 12021ae08745Sheppo return (DDI_FAILURE); 12031ae08745Sheppo } 12041ae08745Sheppo 12051ae08745Sheppo (void) snprintf(name, sizeof (name), "%x", *cnex_regspec); 12061ae08745Sheppo ddi_set_name_addr(child, name); 12071ae08745Sheppo ddi_set_parent_data(child, NULL); 12081ae08745Sheppo ddi_prop_free(cnex_regspec); 12091ae08745Sheppo return (DDI_SUCCESS); 12101ae08745Sheppo } 12111ae08745Sheppo 12121ae08745Sheppo case DDI_CTLOPS_UNINITCHILD: 12131ae08745Sheppo { 12141ae08745Sheppo dev_info_t *child = (dev_info_t *)arg; 12151ae08745Sheppo 12161ae08745Sheppo NDI_CONFIG_DEBUG((CE_NOTE, 12171ae08745Sheppo "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)", 12181ae08745Sheppo ddi_driver_name(child), DEVI(child)->devi_instance)); 12191ae08745Sheppo 12201ae08745Sheppo ddi_set_name_addr(child, NULL); 12211ae08745Sheppo 12221ae08745Sheppo return (DDI_SUCCESS); 12231ae08745Sheppo } 12241ae08745Sheppo 12251ae08745Sheppo case DDI_CTLOPS_DMAPMAPC: 12261ae08745Sheppo case DDI_CTLOPS_REPORTINT: 12271ae08745Sheppo case DDI_CTLOPS_REGSIZE: 12281ae08745Sheppo case DDI_CTLOPS_NREGS: 12291ae08745Sheppo case DDI_CTLOPS_SIDDEV: 12301ae08745Sheppo case DDI_CTLOPS_SLAVEONLY: 12311ae08745Sheppo case DDI_CTLOPS_AFFINITY: 12321ae08745Sheppo case DDI_CTLOPS_POKE: 12331ae08745Sheppo case DDI_CTLOPS_PEEK: 12341ae08745Sheppo /* 12351ae08745Sheppo * These ops correspond to functions that "shouldn't" be called 12361ae08745Sheppo * by a channel-device driver. So we whine when we're called. 12371ae08745Sheppo */ 12381ae08745Sheppo cmn_err(CE_WARN, "%s%d: invalid op (%d) from %s%d\n", 12391ae08745Sheppo ddi_driver_name(dip), ddi_get_instance(dip), ctlop, 12401ae08745Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip)); 12411ae08745Sheppo return (DDI_FAILURE); 12421ae08745Sheppo 12431ae08745Sheppo case DDI_CTLOPS_ATTACH: 12441ae08745Sheppo case DDI_CTLOPS_BTOP: 12451ae08745Sheppo case DDI_CTLOPS_BTOPR: 12461ae08745Sheppo case DDI_CTLOPS_DETACH: 12471ae08745Sheppo case DDI_CTLOPS_DVMAPAGESIZE: 12481ae08745Sheppo case DDI_CTLOPS_IOMIN: 12491ae08745Sheppo case DDI_CTLOPS_POWER: 12501ae08745Sheppo case DDI_CTLOPS_PTOB: 12511ae08745Sheppo default: 12521ae08745Sheppo /* 12531ae08745Sheppo * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up 12541ae08745Sheppo */ 12551ae08745Sheppo return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 12561ae08745Sheppo } 12571ae08745Sheppo } 12581ae08745Sheppo 12590705ae3aSraghuram /* 12600705ae3aSraghuram * cnex_find_chan_dip -- Find the dip of a device that is corresponding 12610705ae3aSraghuram * to the specific channel. Below are the details on how the dip 12620705ae3aSraghuram * is derived. 12630705ae3aSraghuram * 12640705ae3aSraghuram * - In the MD, the cfg-handle is expected to be unique for 12650705ae3aSraghuram * virtual-device nodes that have the same 'name' property value. 12660705ae3aSraghuram * This value is expected to be the same as that of "reg" property 12670705ae3aSraghuram * of the corresponding OBP device node. 12680705ae3aSraghuram * 12690705ae3aSraghuram * - The value of the 'name' property of a virtual-device node 12700705ae3aSraghuram * in the MD is expected to be the same for the corresponding 12710705ae3aSraghuram * OBP device node. 12720705ae3aSraghuram * 12730705ae3aSraghuram * - Find the virtual-device node corresponding to a channel-endpoint 12740705ae3aSraghuram * by walking backwards. Then obtain the values for the 'name' and 12750705ae3aSraghuram * 'cfg-handle' properties. 12760705ae3aSraghuram * 12770705ae3aSraghuram * - Walk all the children of the cnex, find a matching dip which 12780705ae3aSraghuram * has the same 'name' and 'reg' property values. 12790705ae3aSraghuram * 12800705ae3aSraghuram * - The channels that have no corresponding device driver are 12810705ae3aSraghuram * treated as if they correspond to the cnex driver, 12820705ae3aSraghuram * that is, return cnex dip for them. This means, the 12830705ae3aSraghuram * cnex acts as an umbrella device driver. Note, this is 12840705ae3aSraghuram * for 'intrstat' statistics purposes only. As a result of this, 12850705ae3aSraghuram * the 'intrstat' shows cnex as the device that is servicing the 12860705ae3aSraghuram * interrupts corresponding to these channels. 12870705ae3aSraghuram * 12880705ae3aSraghuram * For now, only one such case is known, that is, the channels that 12890705ae3aSraghuram * are used by the "domain-services". 12900705ae3aSraghuram */ 12910705ae3aSraghuram static dev_info_t * 12920705ae3aSraghuram cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id, 12930705ae3aSraghuram md_t *mdp, mde_cookie_t mde) 12940705ae3aSraghuram { 12950705ae3aSraghuram int listsz; 12960705ae3aSraghuram int num_nodes; 12970705ae3aSraghuram int num_devs; 12980705ae3aSraghuram uint64_t cfghdl; 12990705ae3aSraghuram char *md_name; 13000705ae3aSraghuram mde_cookie_t *listp; 13010705ae3aSraghuram dev_info_t *cdip = NULL; 13020705ae3aSraghuram 13030705ae3aSraghuram num_nodes = md_node_count(mdp); 13040705ae3aSraghuram ASSERT(num_nodes > 0); 13050705ae3aSraghuram listsz = num_nodes * sizeof (mde_cookie_t); 13060705ae3aSraghuram listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP); 13070705ae3aSraghuram 13080705ae3aSraghuram num_devs = md_scan_dag(mdp, mde, md_find_name(mdp, "virtual-device"), 13090705ae3aSraghuram md_find_name(mdp, "back"), listp); 13100705ae3aSraghuram ASSERT(num_devs <= 1); 13110705ae3aSraghuram if (num_devs <= 0) { 13120705ae3aSraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): " 13130705ae3aSraghuram "No virtual-device found\n", chan_id); 13140705ae3aSraghuram goto fdip_exit; 13150705ae3aSraghuram } 13160705ae3aSraghuram if (md_get_prop_str(mdp, listp[0], "name", &md_name) != 0) { 13170705ae3aSraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): " 13180705ae3aSraghuram "name property not found\n", chan_id); 13190705ae3aSraghuram goto fdip_exit; 13200705ae3aSraghuram } 13210705ae3aSraghuram 13220705ae3aSraghuram D1("cnex_find_chan_dip: channel(0x%llx): virtual-device " 13230705ae3aSraghuram "name property value = %s\n", chan_id, md_name); 13240705ae3aSraghuram 13250705ae3aSraghuram if (md_get_prop_val(mdp, listp[0], "cfg-handle", &cfghdl) != 0) { 13260705ae3aSraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): virtual-device's " 13270705ae3aSraghuram "cfg-handle property not found\n", chan_id); 13280705ae3aSraghuram goto fdip_exit; 13290705ae3aSraghuram } 13300705ae3aSraghuram 13310705ae3aSraghuram D1("cnex_find_chan_dip:channel(0x%llx): virtual-device cfg-handle " 13320705ae3aSraghuram " property value = 0x%x\n", chan_id, cfghdl); 13330705ae3aSraghuram 13340705ae3aSraghuram for (cdip = ddi_get_child(dip); cdip != NULL; 13350705ae3aSraghuram cdip = ddi_get_next_sibling(cdip)) { 13360705ae3aSraghuram 13370705ae3aSraghuram int *cnex_regspec; 13380705ae3aSraghuram uint32_t reglen; 13390705ae3aSraghuram char *dev_name; 13400705ae3aSraghuram 13410705ae3aSraghuram if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip, 13420705ae3aSraghuram DDI_PROP_DONTPASS, "name", 13430705ae3aSraghuram &dev_name) != DDI_PROP_SUCCESS) { 13440705ae3aSraghuram DWARN("cnex_find_chan_dip: name property not" 13450705ae3aSraghuram " found for dip(0x%p)\n", cdip); 13460705ae3aSraghuram continue; 13470705ae3aSraghuram } 13480705ae3aSraghuram if (strcmp(md_name, dev_name) != 0) { 13490705ae3aSraghuram ddi_prop_free(dev_name); 13500705ae3aSraghuram continue; 13510705ae3aSraghuram } 13520705ae3aSraghuram ddi_prop_free(dev_name); 13530705ae3aSraghuram if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 13540705ae3aSraghuram DDI_PROP_DONTPASS, "reg", 13550705ae3aSraghuram &cnex_regspec, ®len) != DDI_SUCCESS) { 13560705ae3aSraghuram DWARN("cnex_find_chan_dip: reg property not" 13570705ae3aSraghuram " found for dip(0x%p)\n", cdip); 13580705ae3aSraghuram continue; 13590705ae3aSraghuram } 13600705ae3aSraghuram if (*cnex_regspec == cfghdl) { 13610705ae3aSraghuram D1("cnex_find_chan_dip:channel(0x%llx): found " 13620705ae3aSraghuram "dip(0x%p) drvname=%s\n", chan_id, cdip, 13630705ae3aSraghuram ddi_driver_name(cdip)); 1364e2704f8eSarutz ddi_prop_free(cnex_regspec); 13650705ae3aSraghuram break; 13660705ae3aSraghuram } 13670705ae3aSraghuram ddi_prop_free(cnex_regspec); 13680705ae3aSraghuram } 13690705ae3aSraghuram 13700705ae3aSraghuram fdip_exit: 13710705ae3aSraghuram if (cdip == NULL) { 13720705ae3aSraghuram /* 13730705ae3aSraghuram * If a virtual-device node exists but no dip found, 13740705ae3aSraghuram * then for now print a DEBUG error message only. 13750705ae3aSraghuram */ 13760705ae3aSraghuram if (num_devs > 0) { 13770705ae3aSraghuram DERR("cnex_find_chan_dip:channel(0x%llx): " 13780705ae3aSraghuram "No device found\n", chan_id); 13790705ae3aSraghuram } 13800705ae3aSraghuram 13810705ae3aSraghuram /* If no dip was found, return cnex device's dip. */ 13820705ae3aSraghuram cdip = dip; 13830705ae3aSraghuram } 13840705ae3aSraghuram 13850705ae3aSraghuram kmem_free(listp, listsz); 13860705ae3aSraghuram D1("cnex_find_chan_dip:channel(0x%llx): returning dip=0x%p\n", 13870705ae3aSraghuram chan_id, cdip); 13880705ae3aSraghuram return (cdip); 13890705ae3aSraghuram } 13900705ae3aSraghuram 13911ae08745Sheppo /* -------------------------------------------------------------------------- */ 1392