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 /* 22*d66f8315Sjb * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 231ae08745Sheppo * Use is subject to license terms. 241ae08745Sheppo */ 251ae08745Sheppo 261ae08745Sheppo #pragma ident "%Z%%M% %I% %E% SMI" 271ae08745Sheppo 281ae08745Sheppo /* 291ae08745Sheppo * Logical domain channel devices are devices implemented entirely 301ae08745Sheppo * in software; cnex is the nexus for channel-devices. They use 311ae08745Sheppo * the HV channel interfaces via the LDC transport module to send 321ae08745Sheppo * and receive data and to register callbacks. 331ae08745Sheppo */ 341ae08745Sheppo 351ae08745Sheppo #include <sys/types.h> 361ae08745Sheppo #include <sys/cmn_err.h> 371ae08745Sheppo #include <sys/conf.h> 381ae08745Sheppo #include <sys/ddi.h> 391ae08745Sheppo #include <sys/ddi_impldefs.h> 401ae08745Sheppo #include <sys/devops.h> 411ae08745Sheppo #include <sys/instance.h> 421ae08745Sheppo #include <sys/modctl.h> 431ae08745Sheppo #include <sys/open.h> 441ae08745Sheppo #include <sys/stat.h> 451ae08745Sheppo #include <sys/sunddi.h> 461ae08745Sheppo #include <sys/sunndi.h> 471ae08745Sheppo #include <sys/systm.h> 481ae08745Sheppo #include <sys/mkdev.h> 491ae08745Sheppo #include <sys/machsystm.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> 57*d66f8315Sjb #include <sys/hsvc.h> 581ae08745Sheppo 591ae08745Sheppo /* 601ae08745Sheppo * Internal functions/information 611ae08745Sheppo */ 621ae08745Sheppo static struct cnex_pil_map cnex_class_to_pil[] = { 631ae08745Sheppo {LDC_DEV_GENERIC, PIL_3}, 641ae08745Sheppo {LDC_DEV_BLK, PIL_4}, 651ae08745Sheppo {LDC_DEV_BLK_SVC, PIL_3}, 661ae08745Sheppo {LDC_DEV_NT, PIL_6}, 671ae08745Sheppo {LDC_DEV_NT_SVC, PIL_4}, 681ae08745Sheppo {LDC_DEV_SERIAL, PIL_6} 691ae08745Sheppo }; 701ae08745Sheppo #define CNEX_MAX_DEVS (sizeof (cnex_class_to_pil) / \ 711ae08745Sheppo sizeof (cnex_class_to_pil[0])) 721ae08745Sheppo 731ae08745Sheppo #define SUN4V_REG_SPEC2CFG_HDL(x) ((x >> 32) & ~(0xfull << 28)) 741ae08745Sheppo 75a8ea4edeSnarayan static clock_t cnex_wait_usecs = 1000; /* wait time in usecs */ 760d0c8d4bSnarayan static int cnex_wait_retries = 3; 771ae08745Sheppo static void *cnex_state; 781ae08745Sheppo 791ae08745Sheppo static void cnex_intr_redist(void *arg); 801ae08745Sheppo static uint_t cnex_intr_wrapper(caddr_t arg); 811ae08745Sheppo 821ae08745Sheppo /* 831ae08745Sheppo * Debug info 841ae08745Sheppo */ 851ae08745Sheppo #ifdef DEBUG 861ae08745Sheppo 871ae08745Sheppo /* 881ae08745Sheppo * Print debug messages 891ae08745Sheppo * 901ae08745Sheppo * set cnexdbg to 0xf for enabling all msgs 911ae08745Sheppo * 0x8 - Errors 921ae08745Sheppo * 0x4 - Warnings 931ae08745Sheppo * 0x2 - All debug messages 941ae08745Sheppo * 0x1 - Minimal debug messages 951ae08745Sheppo */ 961ae08745Sheppo 971ae08745Sheppo int cnexdbg = 0x8; 981ae08745Sheppo 991ae08745Sheppo static void 1001ae08745Sheppo cnexdebug(const char *fmt, ...) 1011ae08745Sheppo { 1021ae08745Sheppo char buf[512]; 1031ae08745Sheppo va_list ap; 1041ae08745Sheppo 1051ae08745Sheppo va_start(ap, fmt); 1061ae08745Sheppo (void) vsprintf(buf, fmt, ap); 1071ae08745Sheppo va_end(ap); 1081ae08745Sheppo 1091ae08745Sheppo cmn_err(CE_CONT, "%s\n", buf); 1101ae08745Sheppo } 1111ae08745Sheppo 1121ae08745Sheppo #define D1 \ 1131ae08745Sheppo if (cnexdbg & 0x01) \ 1141ae08745Sheppo cnexdebug 1151ae08745Sheppo 1161ae08745Sheppo #define D2 \ 1171ae08745Sheppo if (cnexdbg & 0x02) \ 1181ae08745Sheppo cnexdebug 1191ae08745Sheppo 1201ae08745Sheppo #define DWARN \ 1211ae08745Sheppo if (cnexdbg & 0x04) \ 1221ae08745Sheppo cnexdebug 1231ae08745Sheppo 1241ae08745Sheppo #define DERR \ 1251ae08745Sheppo if (cnexdbg & 0x08) \ 1261ae08745Sheppo cnexdebug 1271ae08745Sheppo 1281ae08745Sheppo #else 1291ae08745Sheppo 1301ae08745Sheppo #define D1 1311ae08745Sheppo #define D2 1321ae08745Sheppo #define DWARN 1331ae08745Sheppo #define DERR 1341ae08745Sheppo 1351ae08745Sheppo #endif 1361ae08745Sheppo 1371ae08745Sheppo /* 1381ae08745Sheppo * Config information 1391ae08745Sheppo */ 1401ae08745Sheppo static int cnex_attach(dev_info_t *, ddi_attach_cmd_t); 1411ae08745Sheppo static int cnex_detach(dev_info_t *, ddi_detach_cmd_t); 1421ae08745Sheppo static int cnex_open(dev_t *, int, int, cred_t *); 1431ae08745Sheppo static int cnex_close(dev_t, int, int, cred_t *); 1441ae08745Sheppo static int cnex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 1451ae08745Sheppo static int cnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 1461ae08745Sheppo void *); 1471ae08745Sheppo 1481ae08745Sheppo static struct bus_ops cnex_bus_ops = { 1491ae08745Sheppo BUSO_REV, 1501ae08745Sheppo nullbusmap, /* bus_map */ 1511ae08745Sheppo NULL, /* bus_get_intrspec */ 1521ae08745Sheppo NULL, /* bus_add_intrspec */ 1531ae08745Sheppo NULL, /* bus_remove_intrspec */ 1541ae08745Sheppo i_ddi_map_fault, /* bus_map_fault */ 1551ae08745Sheppo ddi_no_dma_map, /* bus_dma_map */ 1561ae08745Sheppo ddi_no_dma_allochdl, /* bus_dma_allochdl */ 1571ae08745Sheppo NULL, /* bus_dma_freehdl */ 1581ae08745Sheppo NULL, /* bus_dma_bindhdl */ 1591ae08745Sheppo NULL, /* bus_dma_unbindhdl */ 1601ae08745Sheppo NULL, /* bus_dma_flush */ 1611ae08745Sheppo NULL, /* bus_dma_win */ 1621ae08745Sheppo NULL, /* bus_dma_ctl */ 1631ae08745Sheppo cnex_ctl, /* bus_ctl */ 1641ae08745Sheppo ddi_bus_prop_op, /* bus_prop_op */ 1651ae08745Sheppo 0, /* bus_get_eventcookie */ 1661ae08745Sheppo 0, /* bus_add_eventcall */ 1671ae08745Sheppo 0, /* bus_remove_eventcall */ 1681ae08745Sheppo 0, /* bus_post_event */ 1691ae08745Sheppo NULL, /* bus_intr_ctl */ 1701ae08745Sheppo NULL, /* bus_config */ 1711ae08745Sheppo NULL, /* bus_unconfig */ 1721ae08745Sheppo NULL, /* bus_fm_init */ 1731ae08745Sheppo NULL, /* bus_fm_fini */ 1741ae08745Sheppo NULL, /* bus_fm_access_enter */ 1751ae08745Sheppo NULL, /* bus_fm_access_exit */ 1761ae08745Sheppo NULL, /* bus_power */ 1771ae08745Sheppo NULL /* bus_intr_op */ 1781ae08745Sheppo }; 1791ae08745Sheppo 1801ae08745Sheppo static struct cb_ops cnex_cb_ops = { 1811ae08745Sheppo cnex_open, /* open */ 1821ae08745Sheppo cnex_close, /* close */ 1831ae08745Sheppo nodev, /* strategy */ 1841ae08745Sheppo nodev, /* print */ 1851ae08745Sheppo nodev, /* dump */ 1861ae08745Sheppo nodev, /* read */ 1871ae08745Sheppo nodev, /* write */ 1881ae08745Sheppo cnex_ioctl, /* ioctl */ 1891ae08745Sheppo nodev, /* devmap */ 1901ae08745Sheppo nodev, /* mmap */ 1911ae08745Sheppo nodev, /* segmap */ 1921ae08745Sheppo nochpoll, /* poll */ 1931ae08745Sheppo ddi_prop_op, /* cb_prop_op */ 1941ae08745Sheppo 0, /* streamtab */ 1951ae08745Sheppo D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 1961ae08745Sheppo }; 1971ae08745Sheppo 1981ae08745Sheppo static struct dev_ops cnex_ops = { 1991ae08745Sheppo DEVO_REV, /* devo_rev, */ 2001ae08745Sheppo 0, /* refcnt */ 2011ae08745Sheppo ddi_getinfo_1to1, /* info */ 2021ae08745Sheppo nulldev, /* identify */ 2031ae08745Sheppo nulldev, /* probe */ 2041ae08745Sheppo cnex_attach, /* attach */ 2051ae08745Sheppo cnex_detach, /* detach */ 2061ae08745Sheppo nodev, /* reset */ 2071ae08745Sheppo &cnex_cb_ops, /* driver operations */ 2081ae08745Sheppo &cnex_bus_ops, /* bus operations */ 2091ae08745Sheppo nulldev /* power */ 2101ae08745Sheppo }; 2111ae08745Sheppo 2121ae08745Sheppo /* 2131ae08745Sheppo * Module linkage information for the kernel. 2141ae08745Sheppo */ 2151ae08745Sheppo static struct modldrv modldrv = { 2161ae08745Sheppo &mod_driverops, 217cb112a14Slm "sun4v channel-devices nexus %I%", 2181ae08745Sheppo &cnex_ops, 2191ae08745Sheppo }; 2201ae08745Sheppo 2211ae08745Sheppo static struct modlinkage modlinkage = { 2221ae08745Sheppo MODREV_1, (void *)&modldrv, NULL 2231ae08745Sheppo }; 2241ae08745Sheppo 2251ae08745Sheppo int 2261ae08745Sheppo _init(void) 2271ae08745Sheppo { 2281ae08745Sheppo int err; 229*d66f8315Sjb uint64_t majornum; 230*d66f8315Sjb uint64_t minornum; 231*d66f8315Sjb 232*d66f8315Sjb /* 233*d66f8315Sjb * Check HV intr group api versioning. 234*d66f8315Sjb * Note that cnex assumes interrupt cookies is 235*d66f8315Sjb * in version 1.0 of the intr group api. 236*d66f8315Sjb */ 237*d66f8315Sjb if ((err = hsvc_version(HSVC_GROUP_INTR, &majornum, &minornum)) != 0) { 238*d66f8315Sjb cmn_err(CE_WARN, "cnex: failed to get intr api " 239*d66f8315Sjb "group versioning errno=%d", err); 240*d66f8315Sjb return (err); 241*d66f8315Sjb } else if ((majornum != 1) && (majornum != 2)) { 242*d66f8315Sjb cmn_err(CE_WARN, "cnex: unsupported intr api group: " 243*d66f8315Sjb "maj:0x%lx, min:0x%lx", majornum, minornum); 244*d66f8315Sjb return (ENOTSUP); 245*d66f8315Sjb } 2461ae08745Sheppo 2471ae08745Sheppo if ((err = ddi_soft_state_init(&cnex_state, 2481ae08745Sheppo sizeof (cnex_soft_state_t), 0)) != 0) { 2491ae08745Sheppo return (err); 2501ae08745Sheppo } 2511ae08745Sheppo if ((err = mod_install(&modlinkage)) != 0) { 2521ae08745Sheppo ddi_soft_state_fini(&cnex_state); 2531ae08745Sheppo return (err); 2541ae08745Sheppo } 2551ae08745Sheppo return (0); 2561ae08745Sheppo } 2571ae08745Sheppo 2581ae08745Sheppo int 2591ae08745Sheppo _fini(void) 2601ae08745Sheppo { 2611ae08745Sheppo int err; 2621ae08745Sheppo 2631ae08745Sheppo if ((err = mod_remove(&modlinkage)) != 0) 2641ae08745Sheppo return (err); 2651ae08745Sheppo ddi_soft_state_fini(&cnex_state); 2661ae08745Sheppo return (0); 2671ae08745Sheppo } 2681ae08745Sheppo 2691ae08745Sheppo int 2701ae08745Sheppo _info(struct modinfo *modinfop) 2711ae08745Sheppo { 2721ae08745Sheppo return (mod_info(&modlinkage, modinfop)); 2731ae08745Sheppo } 2741ae08745Sheppo 2751ae08745Sheppo /* 2761ae08745Sheppo * Callback function invoked by the interrupt redistribution 2771ae08745Sheppo * framework. This will redirect interrupts at CPUs that are 2781ae08745Sheppo * currently available in the system. 2791ae08745Sheppo */ 2801ae08745Sheppo static void 2811ae08745Sheppo cnex_intr_redist(void *arg) 2821ae08745Sheppo { 2831ae08745Sheppo cnex_ldc_t *cldcp; 2841ae08745Sheppo cnex_soft_state_t *cnex_ssp = arg; 2851ae08745Sheppo int intr_state; 2861ae08745Sheppo uint64_t cpuid; 2870d0c8d4bSnarayan int rv, retries = 0; 2881ae08745Sheppo 2891ae08745Sheppo ASSERT(cnex_ssp != NULL); 2901ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 2911ae08745Sheppo 2921ae08745Sheppo cldcp = cnex_ssp->clist; 2931ae08745Sheppo while (cldcp != NULL) { 2941ae08745Sheppo 2951ae08745Sheppo mutex_enter(&cldcp->lock); 2961ae08745Sheppo 2971ae08745Sheppo if (cldcp->tx.hdlr) { 2981ae08745Sheppo /* 2991ae08745Sheppo * Don't do anything for disabled interrupts. 3001ae08745Sheppo */ 3011ae08745Sheppo rv = hvldc_intr_getvalid(cnex_ssp->cfghdl, 3021ae08745Sheppo cldcp->tx.ino, &intr_state); 3031ae08745Sheppo if (rv) { 3041ae08745Sheppo DWARN("cnex_intr_redist: tx ino=0x%llx, " 3051ae08745Sheppo "can't get valid\n", cldcp->tx.ino); 3061ae08745Sheppo mutex_exit(&cldcp->lock); 3071ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 3081ae08745Sheppo return; 3091ae08745Sheppo } 3101ae08745Sheppo if (intr_state == HV_INTR_NOTVALID) { 3117636cb21Slm mutex_exit(&cldcp->lock); 3121ae08745Sheppo cldcp = cldcp->next; 3131ae08745Sheppo continue; 3141ae08745Sheppo } 3151ae08745Sheppo 3161ae08745Sheppo cpuid = intr_dist_cpuid(); 3171ae08745Sheppo 3181ae08745Sheppo /* disable interrupts */ 3191ae08745Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, 3201ae08745Sheppo cldcp->tx.ino, HV_INTR_NOTVALID); 3211ae08745Sheppo if (rv) { 3221ae08745Sheppo DWARN("cnex_intr_redist: tx ino=0x%llx, " 3231ae08745Sheppo "can't set valid\n", cldcp->tx.ino); 3241ae08745Sheppo mutex_exit(&cldcp->lock); 3251ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 3261ae08745Sheppo return; 3271ae08745Sheppo } 3281ae08745Sheppo 3291ae08745Sheppo /* 3301ae08745Sheppo * Make a best effort to wait for pending interrupts 3311ae08745Sheppo * to finish. There is not much we can do if we timeout. 3321ae08745Sheppo */ 3330d0c8d4bSnarayan retries = 0; 3341ae08745Sheppo 3351ae08745Sheppo do { 3361ae08745Sheppo rv = hvldc_intr_getstate(cnex_ssp->cfghdl, 3371ae08745Sheppo cldcp->tx.ino, &intr_state); 3381ae08745Sheppo if (rv) { 3391ae08745Sheppo DWARN("cnex_intr_redist: tx ino=0x%llx," 3401ae08745Sheppo "can't get state\n", cldcp->tx.ino); 3411ae08745Sheppo mutex_exit(&cldcp->lock); 3421ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 3431ae08745Sheppo return; 3441ae08745Sheppo } 3451ae08745Sheppo 3460d0c8d4bSnarayan if (intr_state != HV_INTR_DELIVERED_STATE) 3471ae08745Sheppo break; 3481ae08745Sheppo 3490d0c8d4bSnarayan drv_usecwait(cnex_wait_usecs); 3500d0c8d4bSnarayan 3510d0c8d4bSnarayan } while (!panicstr && ++retries <= cnex_wait_retries); 3521ae08745Sheppo 3531ae08745Sheppo (void) hvldc_intr_settarget(cnex_ssp->cfghdl, 3541ae08745Sheppo cldcp->tx.ino, cpuid); 3551ae08745Sheppo (void) hvldc_intr_setvalid(cnex_ssp->cfghdl, 3561ae08745Sheppo cldcp->tx.ino, HV_INTR_VALID); 3571ae08745Sheppo } 3581ae08745Sheppo 3591ae08745Sheppo if (cldcp->rx.hdlr) { 3601ae08745Sheppo /* 3611ae08745Sheppo * Don't do anything for disabled interrupts. 3621ae08745Sheppo */ 3631ae08745Sheppo rv = hvldc_intr_getvalid(cnex_ssp->cfghdl, 3641ae08745Sheppo cldcp->rx.ino, &intr_state); 3651ae08745Sheppo if (rv) { 3661ae08745Sheppo DWARN("cnex_intr_redist: rx ino=0x%llx, " 3671ae08745Sheppo "can't get valid\n", cldcp->rx.ino); 3681ae08745Sheppo mutex_exit(&cldcp->lock); 3691ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 3701ae08745Sheppo return; 3711ae08745Sheppo } 3721ae08745Sheppo if (intr_state == HV_INTR_NOTVALID) { 3737636cb21Slm mutex_exit(&cldcp->lock); 3741ae08745Sheppo cldcp = cldcp->next; 3751ae08745Sheppo continue; 3761ae08745Sheppo } 3771ae08745Sheppo 3781ae08745Sheppo cpuid = intr_dist_cpuid(); 3791ae08745Sheppo 3801ae08745Sheppo /* disable interrupts */ 3811ae08745Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, 3821ae08745Sheppo cldcp->rx.ino, HV_INTR_NOTVALID); 3831ae08745Sheppo if (rv) { 3841ae08745Sheppo DWARN("cnex_intr_redist: rx ino=0x%llx, " 3851ae08745Sheppo "can't set valid\n", cldcp->rx.ino); 3861ae08745Sheppo mutex_exit(&cldcp->lock); 3871ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 3881ae08745Sheppo return; 3891ae08745Sheppo } 3901ae08745Sheppo 3911ae08745Sheppo /* 3921ae08745Sheppo * Make a best effort to wait for pending interrupts 3931ae08745Sheppo * to finish. There is not much we can do if we timeout. 3941ae08745Sheppo */ 3950d0c8d4bSnarayan retries = 0; 3961ae08745Sheppo 3971ae08745Sheppo do { 3981ae08745Sheppo rv = hvldc_intr_getstate(cnex_ssp->cfghdl, 3991ae08745Sheppo cldcp->rx.ino, &intr_state); 4001ae08745Sheppo if (rv) { 4011ae08745Sheppo DWARN("cnex_intr_redist: rx ino=0x%llx," 4020d0c8d4bSnarayan "can't get state\n", cldcp->rx.ino); 4031ae08745Sheppo mutex_exit(&cldcp->lock); 4041ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4051ae08745Sheppo return; 4061ae08745Sheppo } 4071ae08745Sheppo 4080d0c8d4bSnarayan if (intr_state != HV_INTR_DELIVERED_STATE) 4091ae08745Sheppo break; 4101ae08745Sheppo 4110d0c8d4bSnarayan drv_usecwait(cnex_wait_usecs); 4120d0c8d4bSnarayan 4130d0c8d4bSnarayan } while (!panicstr && ++retries <= cnex_wait_retries); 4141ae08745Sheppo 4151ae08745Sheppo (void) hvldc_intr_settarget(cnex_ssp->cfghdl, 4161ae08745Sheppo cldcp->rx.ino, cpuid); 4171ae08745Sheppo (void) hvldc_intr_setvalid(cnex_ssp->cfghdl, 4181ae08745Sheppo cldcp->rx.ino, HV_INTR_VALID); 4191ae08745Sheppo } 4201ae08745Sheppo 4211ae08745Sheppo mutex_exit(&cldcp->lock); 4221ae08745Sheppo 4231ae08745Sheppo /* next channel */ 4241ae08745Sheppo cldcp = cldcp->next; 4251ae08745Sheppo } 4261ae08745Sheppo 4271ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4281ae08745Sheppo } 4291ae08745Sheppo 4301ae08745Sheppo /* 4311ae08745Sheppo * Exported interface to register a LDC endpoint with 4321ae08745Sheppo * the channel nexus 4331ae08745Sheppo */ 4341ae08745Sheppo static int 4351ae08745Sheppo cnex_reg_chan(dev_info_t *dip, uint64_t id, ldc_dev_t devclass) 4361ae08745Sheppo { 4371ae08745Sheppo int idx; 4381ae08745Sheppo cnex_ldc_t *cldcp; 4391ae08745Sheppo int listsz, num_nodes, num_channels; 4401ae08745Sheppo md_t *mdp = NULL; 4411ae08745Sheppo mde_cookie_t rootnode, *listp = NULL; 442cb112a14Slm uint64_t tmp_id; 443cb112a14Slm uint64_t rxino = (uint64_t)-1; 444cb112a14Slm uint64_t txino = (uint64_t)-1; 4451ae08745Sheppo cnex_soft_state_t *cnex_ssp; 4461ae08745Sheppo int status, instance; 4471ae08745Sheppo 4481ae08745Sheppo /* Get device instance and structure */ 4491ae08745Sheppo instance = ddi_get_instance(dip); 4501ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 4511ae08745Sheppo 4521ae08745Sheppo /* Check to see if channel is already registered */ 4531ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 4541ae08745Sheppo cldcp = cnex_ssp->clist; 4551ae08745Sheppo while (cldcp) { 4561ae08745Sheppo if (cldcp->id == id) { 4571ae08745Sheppo DWARN("cnex_reg_chan: channel 0x%llx exists\n", id); 4581ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4591ae08745Sheppo return (EINVAL); 4601ae08745Sheppo } 4611ae08745Sheppo cldcp = cldcp->next; 4621ae08745Sheppo } 4631ae08745Sheppo 4641ae08745Sheppo /* Get the Tx/Rx inos from the MD */ 4651ae08745Sheppo if ((mdp = md_get_handle()) == NULL) { 4661ae08745Sheppo DWARN("cnex_reg_chan: cannot init MD\n"); 4671ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4681ae08745Sheppo return (ENXIO); 4691ae08745Sheppo } 4701ae08745Sheppo num_nodes = md_node_count(mdp); 4711ae08745Sheppo ASSERT(num_nodes > 0); 4721ae08745Sheppo 4731ae08745Sheppo listsz = num_nodes * sizeof (mde_cookie_t); 4741ae08745Sheppo listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP); 4751ae08745Sheppo 4761ae08745Sheppo rootnode = md_root_node(mdp); 4771ae08745Sheppo 4781ae08745Sheppo /* search for all channel_endpoint nodes */ 4791ae08745Sheppo num_channels = md_scan_dag(mdp, rootnode, 4801ae08745Sheppo md_find_name(mdp, "channel-endpoint"), 4811ae08745Sheppo md_find_name(mdp, "fwd"), listp); 4821ae08745Sheppo if (num_channels <= 0) { 4831ae08745Sheppo DWARN("cnex_reg_chan: invalid channel id\n"); 4841ae08745Sheppo kmem_free(listp, listsz); 4851ae08745Sheppo (void) md_fini_handle(mdp); 4861ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4871ae08745Sheppo return (EINVAL); 4881ae08745Sheppo } 4891ae08745Sheppo 4901ae08745Sheppo for (idx = 0; idx < num_channels; idx++) { 4911ae08745Sheppo 4921ae08745Sheppo /* Get the channel ID */ 4931ae08745Sheppo status = md_get_prop_val(mdp, listp[idx], "id", &tmp_id); 4941ae08745Sheppo if (status) { 4951ae08745Sheppo DWARN("cnex_reg_chan: cannot read LDC ID\n"); 4961ae08745Sheppo kmem_free(listp, listsz); 4971ae08745Sheppo (void) md_fini_handle(mdp); 4981ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 4991ae08745Sheppo return (ENXIO); 5001ae08745Sheppo } 5011ae08745Sheppo if (tmp_id != id) 5021ae08745Sheppo continue; 5031ae08745Sheppo 5041ae08745Sheppo /* Get the Tx and Rx ino */ 5051ae08745Sheppo status = md_get_prop_val(mdp, listp[idx], "tx-ino", &txino); 5061ae08745Sheppo if (status) { 5071ae08745Sheppo DWARN("cnex_reg_chan: cannot read Tx ino\n"); 5081ae08745Sheppo kmem_free(listp, listsz); 5091ae08745Sheppo (void) md_fini_handle(mdp); 5101ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 5111ae08745Sheppo return (ENXIO); 5121ae08745Sheppo } 5131ae08745Sheppo status = md_get_prop_val(mdp, listp[idx], "rx-ino", &rxino); 5141ae08745Sheppo if (status) { 5151ae08745Sheppo DWARN("cnex_reg_chan: cannot read Rx ino\n"); 5161ae08745Sheppo kmem_free(listp, listsz); 5171ae08745Sheppo (void) md_fini_handle(mdp); 5181ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 5191ae08745Sheppo return (ENXIO); 5201ae08745Sheppo } 5211ae08745Sheppo } 5221ae08745Sheppo kmem_free(listp, listsz); 5231ae08745Sheppo (void) md_fini_handle(mdp); 5241ae08745Sheppo 525cb112a14Slm /* 526cb112a14Slm * check to see if we looped through the list of channel IDs without 527cb112a14Slm * matching one (i.e. an 'ino' has not been initialised). 528cb112a14Slm */ 529cb112a14Slm if ((rxino == -1) || (txino == -1)) { 530cb112a14Slm DERR("cnex_reg_chan: no ID matching '%llx' in MD\n", id); 531cb112a14Slm mutex_exit(&cnex_ssp->clist_lock); 532cb112a14Slm return (ENOENT); 533cb112a14Slm } 534cb112a14Slm 5351ae08745Sheppo /* Allocate a new channel structure */ 5361ae08745Sheppo cldcp = kmem_zalloc(sizeof (*cldcp), KM_SLEEP); 5371ae08745Sheppo 5381ae08745Sheppo /* Initialize the channel */ 5391ae08745Sheppo mutex_init(&cldcp->lock, NULL, MUTEX_DRIVER, NULL); 5401ae08745Sheppo 5411ae08745Sheppo cldcp->id = id; 5421ae08745Sheppo cldcp->tx.ino = txino; 5431ae08745Sheppo cldcp->rx.ino = rxino; 5441ae08745Sheppo cldcp->devclass = devclass; 5451ae08745Sheppo 5461ae08745Sheppo /* add channel to nexus channel list */ 5471ae08745Sheppo cldcp->next = cnex_ssp->clist; 5481ae08745Sheppo cnex_ssp->clist = cldcp; 5491ae08745Sheppo 5501ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 5511ae08745Sheppo 5521ae08745Sheppo return (0); 5531ae08745Sheppo } 5541ae08745Sheppo 5551ae08745Sheppo /* 5561ae08745Sheppo * Add Tx/Rx interrupt handler for the channel 5571ae08745Sheppo */ 5581ae08745Sheppo static int 5591ae08745Sheppo cnex_add_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype, 5601ae08745Sheppo uint_t (*hdlr)(), caddr_t arg1, caddr_t arg2) 5611ae08745Sheppo { 5621ae08745Sheppo int rv, idx, pil; 5631ae08745Sheppo cnex_ldc_t *cldcp; 5641ae08745Sheppo cnex_intr_t *iinfo; 5651ae08745Sheppo uint64_t cpuid; 5661ae08745Sheppo cnex_soft_state_t *cnex_ssp; 5671ae08745Sheppo int instance; 5681ae08745Sheppo 5691ae08745Sheppo /* Get device instance and structure */ 5701ae08745Sheppo instance = ddi_get_instance(dip); 5711ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 5721ae08745Sheppo 5731ae08745Sheppo /* get channel info */ 5741ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 5751ae08745Sheppo cldcp = cnex_ssp->clist; 5761ae08745Sheppo while (cldcp) { 5771ae08745Sheppo if (cldcp->id == id) 5781ae08745Sheppo break; 5791ae08745Sheppo cldcp = cldcp->next; 5801ae08745Sheppo } 5811ae08745Sheppo if (cldcp == NULL) { 5821ae08745Sheppo DWARN("cnex_add_intr: channel 0x%llx does not exist\n", id); 5831ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 5841ae08745Sheppo return (EINVAL); 5851ae08745Sheppo } 5861ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 5871ae08745Sheppo 5881ae08745Sheppo /* get channel lock */ 5891ae08745Sheppo mutex_enter(&cldcp->lock); 5901ae08745Sheppo 5911ae08745Sheppo /* get interrupt type */ 5921ae08745Sheppo if (itype == CNEX_TX_INTR) { 5931ae08745Sheppo iinfo = &(cldcp->tx); 5941ae08745Sheppo } else if (itype == CNEX_RX_INTR) { 5951ae08745Sheppo iinfo = &(cldcp->rx); 5961ae08745Sheppo } else { 5971ae08745Sheppo DWARN("cnex_add_intr: invalid interrupt type\n", id); 5981ae08745Sheppo mutex_exit(&cldcp->lock); 5991ae08745Sheppo return (EINVAL); 6001ae08745Sheppo } 6011ae08745Sheppo 6021ae08745Sheppo /* check if a handler is already added */ 6031ae08745Sheppo if (iinfo->hdlr != 0) { 6041ae08745Sheppo DWARN("cnex_add_intr: interrupt handler exists\n"); 6051ae08745Sheppo mutex_exit(&cldcp->lock); 6061ae08745Sheppo return (EINVAL); 6071ae08745Sheppo } 6081ae08745Sheppo 6091ae08745Sheppo /* save interrupt handler info */ 6101ae08745Sheppo iinfo->hdlr = hdlr; 6111ae08745Sheppo iinfo->arg1 = arg1; 6121ae08745Sheppo iinfo->arg2 = arg2; 6131ae08745Sheppo 6141ae08745Sheppo iinfo->ssp = cnex_ssp; 6151ae08745Sheppo 6161ae08745Sheppo /* 6171ae08745Sheppo * FIXME - generate the interrupt cookie 6181ae08745Sheppo * using the interrupt registry 6191ae08745Sheppo */ 6201ae08745Sheppo iinfo->icookie = cnex_ssp->cfghdl | iinfo->ino; 6211ae08745Sheppo 6221ae08745Sheppo D1("cnex_add_intr: add hdlr, cfghdl=0x%llx, ino=0x%llx, " 6231ae08745Sheppo "cookie=0x%llx\n", cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie); 6241ae08745Sheppo 6251ae08745Sheppo /* Pick a PIL on the basis of the channel's devclass */ 6261ae08745Sheppo for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) { 6271ae08745Sheppo if (cldcp->devclass == cnex_class_to_pil[idx].devclass) { 6281ae08745Sheppo pil = cnex_class_to_pil[idx].pil; 6291ae08745Sheppo break; 6301ae08745Sheppo } 6311ae08745Sheppo } 6321ae08745Sheppo 6331ae08745Sheppo /* add interrupt to solaris ivec table */ 634b0fc0e77Sgovinda VERIFY(add_ivintr(iinfo->icookie, pil, (intrfunc)cnex_intr_wrapper, 635b0fc0e77Sgovinda (caddr_t)iinfo, NULL, NULL) == 0); 6361ae08745Sheppo 6371ae08745Sheppo /* set the cookie in the HV */ 6381ae08745Sheppo rv = hvldc_intr_setcookie(cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie); 6391ae08745Sheppo 6401ae08745Sheppo /* pick next CPU in the domain for this channel */ 6411ae08745Sheppo cpuid = intr_dist_cpuid(); 6421ae08745Sheppo 6431ae08745Sheppo /* set the target CPU and then enable interrupts */ 6441ae08745Sheppo rv = hvldc_intr_settarget(cnex_ssp->cfghdl, iinfo->ino, cpuid); 6451ae08745Sheppo if (rv) { 6461ae08745Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set target cpu\n", 6471ae08745Sheppo iinfo->ino); 6481ae08745Sheppo goto hv_error; 6491ae08745Sheppo } 6501ae08745Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino, 6511ae08745Sheppo HV_INTR_IDLE_STATE); 6521ae08745Sheppo if (rv) { 6531ae08745Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set state\n", 6541ae08745Sheppo iinfo->ino); 6551ae08745Sheppo goto hv_error; 6561ae08745Sheppo } 6571ae08745Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, iinfo->ino, HV_INTR_VALID); 6581ae08745Sheppo if (rv) { 6591ae08745Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set valid\n", 6601ae08745Sheppo iinfo->ino); 6611ae08745Sheppo goto hv_error; 6621ae08745Sheppo } 6631ae08745Sheppo 6641ae08745Sheppo mutex_exit(&cldcp->lock); 6651ae08745Sheppo return (0); 6661ae08745Sheppo 6671ae08745Sheppo hv_error: 668b0fc0e77Sgovinda (void) rem_ivintr(iinfo->icookie, pil); 6691ae08745Sheppo mutex_exit(&cldcp->lock); 6701ae08745Sheppo return (ENXIO); 6711ae08745Sheppo } 6721ae08745Sheppo 6731ae08745Sheppo 6741ae08745Sheppo /* 6751ae08745Sheppo * Exported interface to unregister a LDC endpoint with 6761ae08745Sheppo * the channel nexus 6771ae08745Sheppo */ 6781ae08745Sheppo static int 6791ae08745Sheppo cnex_unreg_chan(dev_info_t *dip, uint64_t id) 6801ae08745Sheppo { 6811ae08745Sheppo cnex_ldc_t *cldcp, *prev_cldcp; 6821ae08745Sheppo cnex_soft_state_t *cnex_ssp; 6831ae08745Sheppo int instance; 6841ae08745Sheppo 6851ae08745Sheppo /* Get device instance and structure */ 6861ae08745Sheppo instance = ddi_get_instance(dip); 6871ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 6881ae08745Sheppo 6891ae08745Sheppo /* find and remove channel from list */ 6901ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 6911ae08745Sheppo prev_cldcp = NULL; 6921ae08745Sheppo cldcp = cnex_ssp->clist; 6931ae08745Sheppo while (cldcp) { 6941ae08745Sheppo if (cldcp->id == id) 6951ae08745Sheppo break; 6961ae08745Sheppo prev_cldcp = cldcp; 6971ae08745Sheppo cldcp = cldcp->next; 6981ae08745Sheppo } 6991ae08745Sheppo 7001ae08745Sheppo if (cldcp == 0) { 7011ae08745Sheppo DWARN("cnex_unreg_chan: invalid channel %d\n", id); 7021ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7031ae08745Sheppo return (EINVAL); 7041ae08745Sheppo } 7051ae08745Sheppo 7061ae08745Sheppo if (cldcp->tx.hdlr || cldcp->rx.hdlr) { 707cb112a14Slm DWARN("cnex_unreg_chan: handlers still exist: chan %lx\n", id); 7081ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7091ae08745Sheppo return (ENXIO); 7101ae08745Sheppo } 7111ae08745Sheppo 7121ae08745Sheppo if (prev_cldcp) 7131ae08745Sheppo prev_cldcp->next = cldcp->next; 7141ae08745Sheppo else 7151ae08745Sheppo cnex_ssp->clist = cldcp->next; 7161ae08745Sheppo 7171ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7181ae08745Sheppo 7191ae08745Sheppo /* destroy mutex */ 7201ae08745Sheppo mutex_destroy(&cldcp->lock); 7211ae08745Sheppo 7221ae08745Sheppo /* free channel */ 7231ae08745Sheppo kmem_free(cldcp, sizeof (*cldcp)); 7241ae08745Sheppo 7251ae08745Sheppo return (0); 7261ae08745Sheppo } 7271ae08745Sheppo 7281ae08745Sheppo /* 7291ae08745Sheppo * Remove Tx/Rx interrupt handler for the channel 7301ae08745Sheppo */ 7311ae08745Sheppo static int 7321ae08745Sheppo cnex_rem_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype) 7331ae08745Sheppo { 734b0fc0e77Sgovinda int rv, idx, pil; 7351ae08745Sheppo cnex_ldc_t *cldcp; 7361ae08745Sheppo cnex_intr_t *iinfo; 7371ae08745Sheppo cnex_soft_state_t *cnex_ssp; 7381ae08745Sheppo int instance, istate; 7391ae08745Sheppo 7401ae08745Sheppo /* Get device instance and structure */ 7411ae08745Sheppo instance = ddi_get_instance(dip); 7421ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 7431ae08745Sheppo 7441ae08745Sheppo /* get channel info */ 7451ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 7461ae08745Sheppo cldcp = cnex_ssp->clist; 7471ae08745Sheppo while (cldcp) { 7481ae08745Sheppo if (cldcp->id == id) 7491ae08745Sheppo break; 7501ae08745Sheppo cldcp = cldcp->next; 7511ae08745Sheppo } 7521ae08745Sheppo if (cldcp == NULL) { 7531ae08745Sheppo DWARN("cnex_rem_intr: channel 0x%llx does not exist\n", id); 7541ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7551ae08745Sheppo return (EINVAL); 7561ae08745Sheppo } 7571ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 7581ae08745Sheppo 7591ae08745Sheppo /* get rid of the channel intr handler */ 7601ae08745Sheppo mutex_enter(&cldcp->lock); 7611ae08745Sheppo 7621ae08745Sheppo /* get interrupt type */ 7631ae08745Sheppo if (itype == CNEX_TX_INTR) { 7641ae08745Sheppo iinfo = &(cldcp->tx); 7651ae08745Sheppo } else if (itype == CNEX_RX_INTR) { 7661ae08745Sheppo iinfo = &(cldcp->rx); 7671ae08745Sheppo } else { 7681ae08745Sheppo DWARN("cnex_rem_intr: invalid interrupt type\n"); 7691ae08745Sheppo mutex_exit(&cldcp->lock); 7701ae08745Sheppo return (EINVAL); 7711ae08745Sheppo } 7721ae08745Sheppo 7731ae08745Sheppo D1("cnex_rem_intr: interrupt ino=0x%x\n", iinfo->ino); 7741ae08745Sheppo 7751ae08745Sheppo /* check if a handler is already added */ 7761ae08745Sheppo if (iinfo->hdlr == 0) { 7771ae08745Sheppo DWARN("cnex_rem_intr: interrupt handler does not exist\n"); 7781ae08745Sheppo mutex_exit(&cldcp->lock); 7791ae08745Sheppo return (EINVAL); 7801ae08745Sheppo } 7811ae08745Sheppo 7821ae08745Sheppo D1("cnex_rem_intr: set intr to invalid ino=0x%x\n", iinfo->ino); 7831ae08745Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, 7841ae08745Sheppo iinfo->ino, HV_INTR_NOTVALID); 7851ae08745Sheppo if (rv) { 7861ae08745Sheppo DWARN("cnex_rem_intr: cannot set valid ino=%x\n", iinfo->ino); 7871ae08745Sheppo mutex_exit(&cldcp->lock); 7881ae08745Sheppo return (ENXIO); 7891ae08745Sheppo } 7901ae08745Sheppo 7911ae08745Sheppo /* 792a8ea4edeSnarayan * Check if there are pending interrupts. If interrupts are 793a8ea4edeSnarayan * pending return EAGAIN. 7941ae08745Sheppo */ 795a8ea4edeSnarayan rv = hvldc_intr_getstate(cnex_ssp->cfghdl, iinfo->ino, &istate); 796a8ea4edeSnarayan if (rv) { 797a8ea4edeSnarayan DWARN("cnex_rem_intr: ino=0x%llx, cannot get state\n", 798a8ea4edeSnarayan iinfo->ino); 799a8ea4edeSnarayan mutex_exit(&cldcp->lock); 800a8ea4edeSnarayan return (ENXIO); 801a8ea4edeSnarayan } 8021ae08745Sheppo 8031ae08745Sheppo /* if interrupts are still pending print warning */ 8041ae08745Sheppo if (istate != HV_INTR_IDLE_STATE) { 8051ae08745Sheppo DWARN("cnex_rem_intr: cannot remove intr busy ino=%x\n", 8061ae08745Sheppo iinfo->ino); 807d10e4ef2Snarayan mutex_exit(&cldcp->lock); 808d10e4ef2Snarayan return (EAGAIN); 8091ae08745Sheppo } 8101ae08745Sheppo 811b0fc0e77Sgovinda /* Pick a PIL on the basis of the channel's devclass */ 812b0fc0e77Sgovinda for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) { 813b0fc0e77Sgovinda if (cldcp->devclass == cnex_class_to_pil[idx].devclass) { 814b0fc0e77Sgovinda pil = cnex_class_to_pil[idx].pil; 815b0fc0e77Sgovinda break; 816b0fc0e77Sgovinda } 817b0fc0e77Sgovinda } 818b0fc0e77Sgovinda 8191ae08745Sheppo /* remove interrupt */ 820b0fc0e77Sgovinda (void) rem_ivintr(iinfo->icookie, pil); 8211ae08745Sheppo 8221ae08745Sheppo /* clear interrupt info */ 8231ae08745Sheppo bzero(iinfo, sizeof (*iinfo)); 8241ae08745Sheppo 8251ae08745Sheppo mutex_exit(&cldcp->lock); 8261ae08745Sheppo 8271ae08745Sheppo return (0); 8281ae08745Sheppo } 8291ae08745Sheppo 8301ae08745Sheppo 8311ae08745Sheppo /* 8321ae08745Sheppo * Clear pending Tx/Rx interrupt 8331ae08745Sheppo */ 8341ae08745Sheppo static int 8351ae08745Sheppo cnex_clr_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype) 8361ae08745Sheppo { 8371ae08745Sheppo int rv; 8381ae08745Sheppo cnex_ldc_t *cldcp; 8391ae08745Sheppo cnex_intr_t *iinfo; 8401ae08745Sheppo cnex_soft_state_t *cnex_ssp; 8411ae08745Sheppo int instance; 8421ae08745Sheppo 8431ae08745Sheppo /* Get device instance and structure */ 8441ae08745Sheppo instance = ddi_get_instance(dip); 8451ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 8461ae08745Sheppo 8471ae08745Sheppo /* get channel info */ 8481ae08745Sheppo mutex_enter(&cnex_ssp->clist_lock); 8491ae08745Sheppo cldcp = cnex_ssp->clist; 8501ae08745Sheppo while (cldcp) { 8511ae08745Sheppo if (cldcp->id == id) 8521ae08745Sheppo break; 8531ae08745Sheppo cldcp = cldcp->next; 8541ae08745Sheppo } 8551ae08745Sheppo if (cldcp == NULL) { 8561ae08745Sheppo DWARN("cnex_clr_intr: channel 0x%llx does not exist\n", id); 8571ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 8581ae08745Sheppo return (EINVAL); 8591ae08745Sheppo } 8601ae08745Sheppo mutex_exit(&cnex_ssp->clist_lock); 8611ae08745Sheppo 8621ae08745Sheppo mutex_enter(&cldcp->lock); 8631ae08745Sheppo 8641ae08745Sheppo /* get interrupt type */ 8651ae08745Sheppo if (itype == CNEX_TX_INTR) { 8661ae08745Sheppo iinfo = &(cldcp->tx); 8671ae08745Sheppo } else if (itype == CNEX_RX_INTR) { 8681ae08745Sheppo iinfo = &(cldcp->rx); 8691ae08745Sheppo } else { 8707636cb21Slm DWARN("cnex_clr_intr: invalid interrupt type\n"); 8711ae08745Sheppo mutex_exit(&cldcp->lock); 8721ae08745Sheppo return (EINVAL); 8731ae08745Sheppo } 8741ae08745Sheppo 8751ae08745Sheppo D1("cnex_rem_intr: interrupt ino=0x%x\n", iinfo->ino); 8761ae08745Sheppo 8771ae08745Sheppo /* check if a handler is already added */ 8781ae08745Sheppo if (iinfo->hdlr == 0) { 8791ae08745Sheppo DWARN("cnex_clr_intr: interrupt handler does not exist\n"); 8801ae08745Sheppo mutex_exit(&cldcp->lock); 8811ae08745Sheppo return (EINVAL); 8821ae08745Sheppo } 8831ae08745Sheppo 8841ae08745Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino, 8851ae08745Sheppo HV_INTR_IDLE_STATE); 8861ae08745Sheppo if (rv) { 8877636cb21Slm DWARN("cnex_clr_intr: cannot clear interrupt state\n"); 888d10e4ef2Snarayan mutex_exit(&cldcp->lock); 889d10e4ef2Snarayan return (ENXIO); 8901ae08745Sheppo } 8911ae08745Sheppo 8921ae08745Sheppo mutex_exit(&cldcp->lock); 8931ae08745Sheppo 8941ae08745Sheppo return (0); 8951ae08745Sheppo } 8961ae08745Sheppo 8971ae08745Sheppo /* 8981ae08745Sheppo * Channel nexus interrupt handler wrapper 8991ae08745Sheppo */ 9001ae08745Sheppo static uint_t 9011ae08745Sheppo cnex_intr_wrapper(caddr_t arg) 9021ae08745Sheppo { 9031ae08745Sheppo int res; 9041ae08745Sheppo uint_t (*handler)(); 9051ae08745Sheppo caddr_t handler_arg1; 9061ae08745Sheppo caddr_t handler_arg2; 9071ae08745Sheppo cnex_intr_t *iinfo = (cnex_intr_t *)arg; 9081ae08745Sheppo 9091ae08745Sheppo ASSERT(iinfo != NULL); 9101ae08745Sheppo 9111ae08745Sheppo handler = iinfo->hdlr; 9121ae08745Sheppo handler_arg1 = iinfo->arg1; 9131ae08745Sheppo handler_arg2 = iinfo->arg2; 9141ae08745Sheppo 9151ae08745Sheppo D1("cnex_intr_wrapper: ino=0x%llx invoke client handler\n", iinfo->ino); 9161ae08745Sheppo res = (*handler)(handler_arg1, handler_arg2); 9171ae08745Sheppo 9181ae08745Sheppo return (res); 9191ae08745Sheppo } 9201ae08745Sheppo 9211ae08745Sheppo /*ARGSUSED*/ 9221ae08745Sheppo static int 9231ae08745Sheppo cnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 9241ae08745Sheppo { 9251ae08745Sheppo int rv, instance, reglen; 9261ae08745Sheppo cnex_regspec_t *reg_p; 9271ae08745Sheppo ldc_cnex_t cinfo; 9281ae08745Sheppo cnex_soft_state_t *cnex_ssp; 9291ae08745Sheppo 9301ae08745Sheppo switch (cmd) { 9311ae08745Sheppo case DDI_ATTACH: 9321ae08745Sheppo break; 9331ae08745Sheppo case DDI_RESUME: 9341ae08745Sheppo return (DDI_SUCCESS); 9351ae08745Sheppo default: 9361ae08745Sheppo return (DDI_FAILURE); 9371ae08745Sheppo } 9381ae08745Sheppo 9391ae08745Sheppo /* 9401ae08745Sheppo * Get the instance specific soft state structure. 9411ae08745Sheppo * Save the devi for this instance in the soft_state data. 9421ae08745Sheppo */ 9431ae08745Sheppo instance = ddi_get_instance(devi); 9441ae08745Sheppo if (ddi_soft_state_zalloc(cnex_state, instance) != DDI_SUCCESS) 9451ae08745Sheppo return (DDI_FAILURE); 9461ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 9471ae08745Sheppo 9481ae08745Sheppo cnex_ssp->devi = devi; 9491ae08745Sheppo cnex_ssp->clist = NULL; 9501ae08745Sheppo 9511ae08745Sheppo if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 9521ae08745Sheppo "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) { 9531ae08745Sheppo return (DDI_FAILURE); 9541ae08745Sheppo } 9551ae08745Sheppo 9561ae08745Sheppo /* get the sun4v config handle for this device */ 9571ae08745Sheppo cnex_ssp->cfghdl = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr); 9581ae08745Sheppo kmem_free(reg_p, reglen); 9591ae08745Sheppo 9601ae08745Sheppo D1("cnex_attach: cfghdl=0x%llx\n", cnex_ssp->cfghdl); 9611ae08745Sheppo 9621ae08745Sheppo /* init channel list mutex */ 9631ae08745Sheppo mutex_init(&cnex_ssp->clist_lock, NULL, MUTEX_DRIVER, NULL); 9641ae08745Sheppo 9651ae08745Sheppo /* Register with LDC module */ 9661ae08745Sheppo cinfo.dip = devi; 9671ae08745Sheppo cinfo.reg_chan = cnex_reg_chan; 9681ae08745Sheppo cinfo.unreg_chan = cnex_unreg_chan; 9691ae08745Sheppo cinfo.add_intr = cnex_add_intr; 9701ae08745Sheppo cinfo.rem_intr = cnex_rem_intr; 9711ae08745Sheppo cinfo.clr_intr = cnex_clr_intr; 9721ae08745Sheppo 9731ae08745Sheppo /* 9741ae08745Sheppo * LDC register will fail if an nexus instance had already 9751ae08745Sheppo * registered with the LDC framework 9761ae08745Sheppo */ 9771ae08745Sheppo rv = ldc_register(&cinfo); 9781ae08745Sheppo if (rv) { 9791ae08745Sheppo DWARN("cnex_attach: unable to register with LDC\n"); 9801ae08745Sheppo ddi_soft_state_free(cnex_state, instance); 9811ae08745Sheppo mutex_destroy(&cnex_ssp->clist_lock); 9821ae08745Sheppo return (DDI_FAILURE); 9831ae08745Sheppo } 9841ae08745Sheppo 9851ae08745Sheppo if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance, 9861ae08745Sheppo DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 9871ae08745Sheppo ddi_remove_minor_node(devi, NULL); 9881ae08745Sheppo ddi_soft_state_free(cnex_state, instance); 9891ae08745Sheppo mutex_destroy(&cnex_ssp->clist_lock); 9901ae08745Sheppo return (DDI_FAILURE); 9911ae08745Sheppo } 9921ae08745Sheppo 9931ae08745Sheppo /* Add interrupt redistribution callback. */ 9941ae08745Sheppo intr_dist_add(cnex_intr_redist, cnex_ssp); 9951ae08745Sheppo 9961ae08745Sheppo ddi_report_dev(devi); 9971ae08745Sheppo return (DDI_SUCCESS); 9981ae08745Sheppo } 9991ae08745Sheppo 10001ae08745Sheppo /*ARGSUSED*/ 10011ae08745Sheppo static int 10021ae08745Sheppo cnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 10031ae08745Sheppo { 10041ae08745Sheppo int instance; 10051ae08745Sheppo ldc_cnex_t cinfo; 10061ae08745Sheppo cnex_soft_state_t *cnex_ssp; 10071ae08745Sheppo 10081ae08745Sheppo switch (cmd) { 10091ae08745Sheppo case DDI_DETACH: 10101ae08745Sheppo break; 10111ae08745Sheppo case DDI_SUSPEND: 10121ae08745Sheppo return (DDI_SUCCESS); 10131ae08745Sheppo default: 10141ae08745Sheppo return (DDI_FAILURE); 10151ae08745Sheppo } 10161ae08745Sheppo 10171ae08745Sheppo instance = ddi_get_instance(devi); 10181ae08745Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 10191ae08745Sheppo 10201ae08745Sheppo /* check if there are any channels still registered */ 10211ae08745Sheppo if (cnex_ssp->clist) { 10221ae08745Sheppo cmn_err(CE_WARN, "?cnex_dettach: channels registered %d\n", 10231ae08745Sheppo ddi_get_instance(devi)); 10241ae08745Sheppo return (DDI_FAILURE); 10251ae08745Sheppo } 10261ae08745Sheppo 10271ae08745Sheppo /* Unregister with LDC module */ 10281ae08745Sheppo cinfo.dip = devi; 10291ae08745Sheppo (void) ldc_unregister(&cinfo); 10301ae08745Sheppo 10311ae08745Sheppo /* Remove interrupt redistribution callback. */ 10321ae08745Sheppo intr_dist_rem(cnex_intr_redist, cnex_ssp); 10331ae08745Sheppo 10341ae08745Sheppo /* destroy mutex */ 10351ae08745Sheppo mutex_destroy(&cnex_ssp->clist_lock); 10361ae08745Sheppo 10371ae08745Sheppo /* free soft state structure */ 10381ae08745Sheppo ddi_soft_state_free(cnex_state, instance); 10391ae08745Sheppo 10401ae08745Sheppo return (DDI_SUCCESS); 10411ae08745Sheppo } 10421ae08745Sheppo 10431ae08745Sheppo /*ARGSUSED*/ 10441ae08745Sheppo static int 10451ae08745Sheppo cnex_open(dev_t *devp, int flags, int otyp, cred_t *credp) 10461ae08745Sheppo { 10471ae08745Sheppo int instance; 10481ae08745Sheppo 10491ae08745Sheppo if (otyp != OTYP_CHR) 10501ae08745Sheppo return (EINVAL); 10511ae08745Sheppo 10521ae08745Sheppo instance = getminor(*devp); 10531ae08745Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL) 10541ae08745Sheppo return (ENXIO); 10551ae08745Sheppo 10561ae08745Sheppo return (0); 10571ae08745Sheppo } 10581ae08745Sheppo 10591ae08745Sheppo /*ARGSUSED*/ 10601ae08745Sheppo static int 10611ae08745Sheppo cnex_close(dev_t dev, int flags, int otyp, cred_t *credp) 10621ae08745Sheppo { 10631ae08745Sheppo int instance; 10641ae08745Sheppo 10651ae08745Sheppo if (otyp != OTYP_CHR) 10661ae08745Sheppo return (EINVAL); 10671ae08745Sheppo 10681ae08745Sheppo instance = getminor(dev); 10691ae08745Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL) 10701ae08745Sheppo return (ENXIO); 10711ae08745Sheppo 10721ae08745Sheppo return (0); 10731ae08745Sheppo } 10741ae08745Sheppo 10751ae08745Sheppo /*ARGSUSED*/ 10761ae08745Sheppo static int 10771ae08745Sheppo cnex_ioctl(dev_t dev, 10781ae08745Sheppo int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 10791ae08745Sheppo { 10801ae08745Sheppo int instance; 10811ae08745Sheppo cnex_soft_state_t *cnex_ssp; 10821ae08745Sheppo 10831ae08745Sheppo instance = getminor(dev); 10841ae08745Sheppo if ((cnex_ssp = ddi_get_soft_state(cnex_state, instance)) == NULL) 10851ae08745Sheppo return (ENXIO); 10861ae08745Sheppo ASSERT(cnex_ssp->devi); 10871ae08745Sheppo return (ndi_devctl_ioctl(cnex_ssp->devi, cmd, arg, mode, 0)); 10881ae08745Sheppo } 10891ae08745Sheppo 10901ae08745Sheppo static int 10911ae08745Sheppo cnex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 10921ae08745Sheppo void *arg, void *result) 10931ae08745Sheppo { 10941ae08745Sheppo char name[MAXNAMELEN]; 10951ae08745Sheppo uint32_t reglen; 10961ae08745Sheppo int *cnex_regspec; 10971ae08745Sheppo 10981ae08745Sheppo switch (ctlop) { 10991ae08745Sheppo case DDI_CTLOPS_REPORTDEV: 11001ae08745Sheppo if (rdip == NULL) 11011ae08745Sheppo return (DDI_FAILURE); 11021ae08745Sheppo cmn_err(CE_CONT, "?channel-device: %s%d\n", 11031ae08745Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip)); 11041ae08745Sheppo return (DDI_SUCCESS); 11051ae08745Sheppo 11061ae08745Sheppo case DDI_CTLOPS_INITCHILD: 11071ae08745Sheppo { 11081ae08745Sheppo dev_info_t *child = (dev_info_t *)arg; 11091ae08745Sheppo 11101ae08745Sheppo if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 11111ae08745Sheppo DDI_PROP_DONTPASS, "reg", 11121ae08745Sheppo &cnex_regspec, ®len) != DDI_SUCCESS) { 11131ae08745Sheppo return (DDI_FAILURE); 11141ae08745Sheppo } 11151ae08745Sheppo 11161ae08745Sheppo (void) snprintf(name, sizeof (name), "%x", *cnex_regspec); 11171ae08745Sheppo ddi_set_name_addr(child, name); 11181ae08745Sheppo ddi_set_parent_data(child, NULL); 11191ae08745Sheppo ddi_prop_free(cnex_regspec); 11201ae08745Sheppo return (DDI_SUCCESS); 11211ae08745Sheppo } 11221ae08745Sheppo 11231ae08745Sheppo case DDI_CTLOPS_UNINITCHILD: 11241ae08745Sheppo { 11251ae08745Sheppo dev_info_t *child = (dev_info_t *)arg; 11261ae08745Sheppo 11271ae08745Sheppo NDI_CONFIG_DEBUG((CE_NOTE, 11281ae08745Sheppo "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)", 11291ae08745Sheppo ddi_driver_name(child), DEVI(child)->devi_instance)); 11301ae08745Sheppo 11311ae08745Sheppo ddi_set_name_addr(child, NULL); 11321ae08745Sheppo 11331ae08745Sheppo return (DDI_SUCCESS); 11341ae08745Sheppo } 11351ae08745Sheppo 11361ae08745Sheppo case DDI_CTLOPS_DMAPMAPC: 11371ae08745Sheppo case DDI_CTLOPS_REPORTINT: 11381ae08745Sheppo case DDI_CTLOPS_REGSIZE: 11391ae08745Sheppo case DDI_CTLOPS_NREGS: 11401ae08745Sheppo case DDI_CTLOPS_SIDDEV: 11411ae08745Sheppo case DDI_CTLOPS_SLAVEONLY: 11421ae08745Sheppo case DDI_CTLOPS_AFFINITY: 11431ae08745Sheppo case DDI_CTLOPS_POKE: 11441ae08745Sheppo case DDI_CTLOPS_PEEK: 11451ae08745Sheppo /* 11461ae08745Sheppo * These ops correspond to functions that "shouldn't" be called 11471ae08745Sheppo * by a channel-device driver. So we whine when we're called. 11481ae08745Sheppo */ 11491ae08745Sheppo cmn_err(CE_WARN, "%s%d: invalid op (%d) from %s%d\n", 11501ae08745Sheppo ddi_driver_name(dip), ddi_get_instance(dip), ctlop, 11511ae08745Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip)); 11521ae08745Sheppo return (DDI_FAILURE); 11531ae08745Sheppo 11541ae08745Sheppo case DDI_CTLOPS_ATTACH: 11551ae08745Sheppo case DDI_CTLOPS_BTOP: 11561ae08745Sheppo case DDI_CTLOPS_BTOPR: 11571ae08745Sheppo case DDI_CTLOPS_DETACH: 11581ae08745Sheppo case DDI_CTLOPS_DVMAPAGESIZE: 11591ae08745Sheppo case DDI_CTLOPS_IOMIN: 11601ae08745Sheppo case DDI_CTLOPS_POWER: 11611ae08745Sheppo case DDI_CTLOPS_PTOB: 11621ae08745Sheppo default: 11631ae08745Sheppo /* 11641ae08745Sheppo * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up 11651ae08745Sheppo */ 11661ae08745Sheppo return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 11671ae08745Sheppo } 11681ae08745Sheppo } 11691ae08745Sheppo 11701ae08745Sheppo /* -------------------------------------------------------------------------- */ 1171