11991Sheppo /* 21991Sheppo * CDDL HEADER START 31991Sheppo * 41991Sheppo * The contents of this file are subject to the terms of the 51991Sheppo * Common Development and Distribution License (the "License"). 61991Sheppo * You may not use this file except in compliance with the License. 71991Sheppo * 81991Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91991Sheppo * or http://www.opensolaris.org/os/licensing. 101991Sheppo * See the License for the specific language governing permissions 111991Sheppo * and limitations under the License. 121991Sheppo * 131991Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141991Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151991Sheppo * If applicable, add the following below this CDDL HEADER, with the 161991Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171991Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181991Sheppo * 191991Sheppo * CDDL HEADER END 201991Sheppo */ 211991Sheppo /* 22*6408Sha137994 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 231991Sheppo * Use is subject to license terms. 241991Sheppo */ 251991Sheppo 261991Sheppo #pragma ident "%Z%%M% %I% %E% SMI" 271991Sheppo 281991Sheppo /* 291991Sheppo * Logical domain channel devices are devices implemented entirely 301991Sheppo * in software; cnex is the nexus for channel-devices. They use 311991Sheppo * the HV channel interfaces via the LDC transport module to send 321991Sheppo * and receive data and to register callbacks. 331991Sheppo */ 341991Sheppo 351991Sheppo #include <sys/types.h> 361991Sheppo #include <sys/cmn_err.h> 371991Sheppo #include <sys/conf.h> 381991Sheppo #include <sys/ddi.h> 391991Sheppo #include <sys/ddi_impldefs.h> 401991Sheppo #include <sys/devops.h> 411991Sheppo #include <sys/instance.h> 421991Sheppo #include <sys/modctl.h> 431991Sheppo #include <sys/open.h> 441991Sheppo #include <sys/stat.h> 451991Sheppo #include <sys/sunddi.h> 461991Sheppo #include <sys/sunndi.h> 471991Sheppo #include <sys/systm.h> 481991Sheppo #include <sys/mkdev.h> 491991Sheppo #include <sys/machsystm.h> 505513Slm66018 #include <sys/intreg.h> 511991Sheppo #include <sys/intr.h> 521991Sheppo #include <sys/ddi_intr_impl.h> 531991Sheppo #include <sys/ivintr.h> 541991Sheppo #include <sys/hypervisor_api.h> 551991Sheppo #include <sys/ldc.h> 561991Sheppo #include <sys/cnex.h> 571991Sheppo #include <sys/mach_descrip.h> 584423Sjb145095 #include <sys/hsvc.h> 594750Sraghuram #include <sys/sdt.h> 601991Sheppo 611991Sheppo /* 621991Sheppo * Internal functions/information 631991Sheppo */ 64*6408Sha137994 static struct cnex_intr_map cnex_class_to_intr[] = { 65*6408Sha137994 {LDC_DEV_GENERIC, PIL_3, 0}, 66*6408Sha137994 {LDC_DEV_BLK, PIL_4, 10}, 67*6408Sha137994 {LDC_DEV_BLK_SVC, PIL_3, 10}, 68*6408Sha137994 {LDC_DEV_NT, PIL_6, 35}, 69*6408Sha137994 {LDC_DEV_NT_SVC, PIL_4, 35}, 70*6408Sha137994 {LDC_DEV_SERIAL, PIL_6, 0} 711991Sheppo }; 72*6408Sha137994 #define CNEX_MAX_DEVS (sizeof (cnex_class_to_intr) / \ 73*6408Sha137994 sizeof (cnex_class_to_intr[0])) 74*6408Sha137994 75*6408Sha137994 #define CNEX_TX_INTR_WEIGHT 0 761991Sheppo 771991Sheppo #define SUN4V_REG_SPEC2CFG_HDL(x) ((x >> 32) & ~(0xfull << 28)) 781991Sheppo 792841Snarayan static clock_t cnex_wait_usecs = 1000; /* wait time in usecs */ 803078Snarayan static int cnex_wait_retries = 3; 811991Sheppo static void *cnex_state; 821991Sheppo 831991Sheppo static uint_t cnex_intr_wrapper(caddr_t arg); 844750Sraghuram static dev_info_t *cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id, 854750Sraghuram md_t *mdp, mde_cookie_t mde); 861991Sheppo 871991Sheppo /* 88*6408Sha137994 * Channel Interrupt Distribution 89*6408Sha137994 * 90*6408Sha137994 * In order to balance interrupts among available CPUs, we use 91*6408Sha137994 * the intr_dist_cpuid_{add,remove}_device_weight() interface to 92*6408Sha137994 * assign weights to channel interrupts. These weights, which are 93*6408Sha137994 * defined in the cnex_intr_map structure, influence which CPU 94*6408Sha137994 * is returned by intr_dist_cpuid() when called via the cnex 95*6408Sha137994 * interrupt redistribution callback cnex_intr_redist(). 96*6408Sha137994 * Interrupts for VIO devclass channels are given more weight than 97*6408Sha137994 * other interrupts because they are expected to occur more 98*6408Sha137994 * frequently and have a larger impact on overall performance. 99*6408Sha137994 * Transmit interrupts are given a zero weight because they are 100*6408Sha137994 * not used. 101*6408Sha137994 * 102*6408Sha137994 * The interrupt weights influence the target CPU selection when 103*6408Sha137994 * interrupts are redistributed and when they are added. However, 104*6408Sha137994 * removal of interrupts can unbalance the distribution even if 105*6408Sha137994 * they are removed in converse order--compared to the order they 106*6408Sha137994 * are added. This can occur when interrupts are removed after 107*6408Sha137994 * redistribution occurs. 108*6408Sha137994 * 109*6408Sha137994 * Channel interrupt weights affect interrupt-CPU distribution 110*6408Sha137994 * relative to other weighted interrupts on the system. For VIO 111*6408Sha137994 * devclass channels, values are chosen to match those used by 112*6408Sha137994 * the PCI express nexus driver for net and storage devices. 113*6408Sha137994 */ 114*6408Sha137994 static void cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight); 115*6408Sha137994 static int cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo); 116*6408Sha137994 static int cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo); 117*6408Sha137994 static int32_t cnex_class_weight(ldc_dev_t devclass); 118*6408Sha137994 119*6408Sha137994 /* 1201991Sheppo * Debug info 1211991Sheppo */ 1221991Sheppo #ifdef DEBUG 1231991Sheppo 1241991Sheppo /* 1251991Sheppo * Print debug messages 1261991Sheppo * 1271991Sheppo * set cnexdbg to 0xf for enabling all msgs 1281991Sheppo * 0x8 - Errors 1291991Sheppo * 0x4 - Warnings 1301991Sheppo * 0x2 - All debug messages 1311991Sheppo * 0x1 - Minimal debug messages 1321991Sheppo */ 1331991Sheppo 1341991Sheppo int cnexdbg = 0x8; 1351991Sheppo 1361991Sheppo static void 1371991Sheppo cnexdebug(const char *fmt, ...) 1381991Sheppo { 1391991Sheppo char buf[512]; 1401991Sheppo va_list ap; 1411991Sheppo 1421991Sheppo va_start(ap, fmt); 1431991Sheppo (void) vsprintf(buf, fmt, ap); 1441991Sheppo va_end(ap); 1451991Sheppo 1461991Sheppo cmn_err(CE_CONT, "%s\n", buf); 1471991Sheppo } 1481991Sheppo 1491991Sheppo #define D1 \ 1501991Sheppo if (cnexdbg & 0x01) \ 1511991Sheppo cnexdebug 1521991Sheppo 1531991Sheppo #define D2 \ 1541991Sheppo if (cnexdbg & 0x02) \ 1551991Sheppo cnexdebug 1561991Sheppo 1571991Sheppo #define DWARN \ 1581991Sheppo if (cnexdbg & 0x04) \ 1591991Sheppo cnexdebug 1601991Sheppo 1611991Sheppo #define DERR \ 1621991Sheppo if (cnexdbg & 0x08) \ 1631991Sheppo cnexdebug 1641991Sheppo 1651991Sheppo #else 1661991Sheppo 1671991Sheppo #define D1 1681991Sheppo #define D2 1691991Sheppo #define DWARN 1701991Sheppo #define DERR 1711991Sheppo 1721991Sheppo #endif 1731991Sheppo 1741991Sheppo /* 1751991Sheppo * Config information 1761991Sheppo */ 1771991Sheppo static int cnex_attach(dev_info_t *, ddi_attach_cmd_t); 1781991Sheppo static int cnex_detach(dev_info_t *, ddi_detach_cmd_t); 1791991Sheppo static int cnex_open(dev_t *, int, int, cred_t *); 1801991Sheppo static int cnex_close(dev_t, int, int, cred_t *); 1811991Sheppo static int cnex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 1821991Sheppo static int cnex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 1831991Sheppo void *); 1841991Sheppo 1851991Sheppo static struct bus_ops cnex_bus_ops = { 1861991Sheppo BUSO_REV, 1871991Sheppo nullbusmap, /* bus_map */ 1881991Sheppo NULL, /* bus_get_intrspec */ 1891991Sheppo NULL, /* bus_add_intrspec */ 1901991Sheppo NULL, /* bus_remove_intrspec */ 1911991Sheppo i_ddi_map_fault, /* bus_map_fault */ 1921991Sheppo ddi_no_dma_map, /* bus_dma_map */ 1931991Sheppo ddi_no_dma_allochdl, /* bus_dma_allochdl */ 1941991Sheppo NULL, /* bus_dma_freehdl */ 1951991Sheppo NULL, /* bus_dma_bindhdl */ 1961991Sheppo NULL, /* bus_dma_unbindhdl */ 1971991Sheppo NULL, /* bus_dma_flush */ 1981991Sheppo NULL, /* bus_dma_win */ 1991991Sheppo NULL, /* bus_dma_ctl */ 2001991Sheppo cnex_ctl, /* bus_ctl */ 2011991Sheppo ddi_bus_prop_op, /* bus_prop_op */ 2021991Sheppo 0, /* bus_get_eventcookie */ 2031991Sheppo 0, /* bus_add_eventcall */ 2041991Sheppo 0, /* bus_remove_eventcall */ 2051991Sheppo 0, /* bus_post_event */ 2061991Sheppo NULL, /* bus_intr_ctl */ 2071991Sheppo NULL, /* bus_config */ 2081991Sheppo NULL, /* bus_unconfig */ 2091991Sheppo NULL, /* bus_fm_init */ 2101991Sheppo NULL, /* bus_fm_fini */ 2111991Sheppo NULL, /* bus_fm_access_enter */ 2121991Sheppo NULL, /* bus_fm_access_exit */ 2131991Sheppo NULL, /* bus_power */ 2141991Sheppo NULL /* bus_intr_op */ 2151991Sheppo }; 2161991Sheppo 2171991Sheppo static struct cb_ops cnex_cb_ops = { 2181991Sheppo cnex_open, /* open */ 2191991Sheppo cnex_close, /* close */ 2201991Sheppo nodev, /* strategy */ 2211991Sheppo nodev, /* print */ 2221991Sheppo nodev, /* dump */ 2231991Sheppo nodev, /* read */ 2241991Sheppo nodev, /* write */ 2251991Sheppo cnex_ioctl, /* ioctl */ 2261991Sheppo nodev, /* devmap */ 2271991Sheppo nodev, /* mmap */ 2281991Sheppo nodev, /* segmap */ 2291991Sheppo nochpoll, /* poll */ 2301991Sheppo ddi_prop_op, /* cb_prop_op */ 2311991Sheppo 0, /* streamtab */ 2321991Sheppo D_MP | D_NEW | D_HOTPLUG /* Driver compatibility flag */ 2331991Sheppo }; 2341991Sheppo 2351991Sheppo static struct dev_ops cnex_ops = { 2361991Sheppo DEVO_REV, /* devo_rev, */ 2371991Sheppo 0, /* refcnt */ 2381991Sheppo ddi_getinfo_1to1, /* info */ 2391991Sheppo nulldev, /* identify */ 2401991Sheppo nulldev, /* probe */ 2411991Sheppo cnex_attach, /* attach */ 2421991Sheppo cnex_detach, /* detach */ 2431991Sheppo nodev, /* reset */ 2441991Sheppo &cnex_cb_ops, /* driver operations */ 2451991Sheppo &cnex_bus_ops, /* bus operations */ 2461991Sheppo nulldev /* power */ 2471991Sheppo }; 2481991Sheppo 2491991Sheppo /* 2501991Sheppo * Module linkage information for the kernel. 2511991Sheppo */ 2521991Sheppo static struct modldrv modldrv = { 2531991Sheppo &mod_driverops, 2545661Slm66018 "sun4v channel-devices nexus 1.11", 2551991Sheppo &cnex_ops, 2561991Sheppo }; 2571991Sheppo 2581991Sheppo static struct modlinkage modlinkage = { 2591991Sheppo MODREV_1, (void *)&modldrv, NULL 2601991Sheppo }; 2611991Sheppo 2621991Sheppo int 2631991Sheppo _init(void) 2641991Sheppo { 2651991Sheppo int err; 2664423Sjb145095 uint64_t majornum; 2674423Sjb145095 uint64_t minornum; 2684423Sjb145095 2694423Sjb145095 /* 2704423Sjb145095 * Check HV intr group api versioning. 2714423Sjb145095 * Note that cnex assumes interrupt cookies is 2724423Sjb145095 * in version 1.0 of the intr group api. 2734423Sjb145095 */ 2744423Sjb145095 if ((err = hsvc_version(HSVC_GROUP_INTR, &majornum, &minornum)) != 0) { 2754423Sjb145095 cmn_err(CE_WARN, "cnex: failed to get intr api " 2764423Sjb145095 "group versioning errno=%d", err); 2774423Sjb145095 return (err); 2784423Sjb145095 } else if ((majornum != 1) && (majornum != 2)) { 2794423Sjb145095 cmn_err(CE_WARN, "cnex: unsupported intr api group: " 2804423Sjb145095 "maj:0x%lx, min:0x%lx", majornum, minornum); 2814423Sjb145095 return (ENOTSUP); 2824423Sjb145095 } 2831991Sheppo 2841991Sheppo if ((err = ddi_soft_state_init(&cnex_state, 2854750Sraghuram sizeof (cnex_soft_state_t), 0)) != 0) { 2861991Sheppo return (err); 2871991Sheppo } 2881991Sheppo if ((err = mod_install(&modlinkage)) != 0) { 2891991Sheppo ddi_soft_state_fini(&cnex_state); 2901991Sheppo return (err); 2911991Sheppo } 2921991Sheppo return (0); 2931991Sheppo } 2941991Sheppo 2951991Sheppo int 2961991Sheppo _fini(void) 2971991Sheppo { 2981991Sheppo int err; 2991991Sheppo 3001991Sheppo if ((err = mod_remove(&modlinkage)) != 0) 3011991Sheppo return (err); 3021991Sheppo ddi_soft_state_fini(&cnex_state); 3031991Sheppo return (0); 3041991Sheppo } 3051991Sheppo 3061991Sheppo int 3071991Sheppo _info(struct modinfo *modinfop) 3081991Sheppo { 3091991Sheppo return (mod_info(&modlinkage, modinfop)); 3101991Sheppo } 3111991Sheppo 3121991Sheppo /* 3131991Sheppo * Callback function invoked by the interrupt redistribution 3141991Sheppo * framework. This will redirect interrupts at CPUs that are 3151991Sheppo * currently available in the system. 316*6408Sha137994 * 317*6408Sha137994 * Note: any interrupts with weight greater than or equal to 318*6408Sha137994 * weight_max must be redistributed when this callback is 319*6408Sha137994 * invoked with (weight == weight_max) which will be once per 320*6408Sha137994 * redistribution. 3211991Sheppo */ 322*6408Sha137994 /*ARGSUSED*/ 3231991Sheppo static void 324*6408Sha137994 cnex_intr_redist(void *arg, int32_t weight_max, int32_t weight) 3251991Sheppo { 3261991Sheppo cnex_ldc_t *cldcp; 3271991Sheppo cnex_soft_state_t *cnex_ssp = arg; 3281991Sheppo 3291991Sheppo ASSERT(cnex_ssp != NULL); 3301991Sheppo mutex_enter(&cnex_ssp->clist_lock); 3311991Sheppo 3321991Sheppo cldcp = cnex_ssp->clist; 3331991Sheppo while (cldcp != NULL) { 3341991Sheppo 3351991Sheppo mutex_enter(&cldcp->lock); 3361991Sheppo 337*6408Sha137994 if (cldcp->tx.hdlr && (cldcp->tx.weight == weight || 338*6408Sha137994 (weight_max == weight && cldcp->tx.weight > weight))) { 339*6408Sha137994 (void) cnex_intr_new_cpu(cnex_ssp, &cldcp->tx); 3401991Sheppo } 3411991Sheppo 342*6408Sha137994 if (cldcp->rx.hdlr && (cldcp->rx.weight == weight || 343*6408Sha137994 (weight_max == weight && cldcp->rx.weight > weight))) { 344*6408Sha137994 (void) cnex_intr_new_cpu(cnex_ssp, &cldcp->rx); 3451991Sheppo } 3461991Sheppo 3471991Sheppo mutex_exit(&cldcp->lock); 3481991Sheppo 3491991Sheppo /* next channel */ 3501991Sheppo cldcp = cldcp->next; 3511991Sheppo } 3521991Sheppo 3531991Sheppo mutex_exit(&cnex_ssp->clist_lock); 3541991Sheppo } 3551991Sheppo 3561991Sheppo /* 357*6408Sha137994 * Internal function to replace the CPU used by an interrupt 358*6408Sha137994 * during interrupt redistribution. 359*6408Sha137994 */ 360*6408Sha137994 static int 361*6408Sha137994 cnex_intr_new_cpu(cnex_soft_state_t *ssp, cnex_intr_t *iinfo) 362*6408Sha137994 { 363*6408Sha137994 int intr_state; 364*6408Sha137994 int rv; 365*6408Sha137994 366*6408Sha137994 /* Determine if the interrupt is enabled */ 367*6408Sha137994 rv = hvldc_intr_getvalid(ssp->cfghdl, iinfo->ino, &intr_state); 368*6408Sha137994 if (rv) { 369*6408Sha137994 DWARN("cnex_intr_new_cpu: rx ino=0x%llx, can't get valid\n", 370*6408Sha137994 iinfo->ino); 371*6408Sha137994 return (rv); 372*6408Sha137994 } 373*6408Sha137994 374*6408Sha137994 /* If it is enabled, disable it */ 375*6408Sha137994 if (intr_state == HV_INTR_VALID) { 376*6408Sha137994 rv = cnex_intr_dis_wait(ssp, iinfo); 377*6408Sha137994 if (rv) { 378*6408Sha137994 return (rv); 379*6408Sha137994 } 380*6408Sha137994 } 381*6408Sha137994 382*6408Sha137994 /* Target the interrupt at a new CPU. */ 383*6408Sha137994 iinfo->cpuid = intr_dist_cpuid(); 384*6408Sha137994 (void) hvldc_intr_settarget(ssp->cfghdl, iinfo->ino, iinfo->cpuid); 385*6408Sha137994 intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip, 386*6408Sha137994 iinfo->weight); 387*6408Sha137994 388*6408Sha137994 /* Re-enable the interrupt if it was enabled */ 389*6408Sha137994 if (intr_state == HV_INTR_VALID) { 390*6408Sha137994 (void) hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino, 391*6408Sha137994 HV_INTR_VALID); 392*6408Sha137994 } 393*6408Sha137994 394*6408Sha137994 return (0); 395*6408Sha137994 } 396*6408Sha137994 397*6408Sha137994 /* 398*6408Sha137994 * Internal function to disable an interrupt and wait 399*6408Sha137994 * for any pending interrupts to finish. 400*6408Sha137994 */ 401*6408Sha137994 static int 402*6408Sha137994 cnex_intr_dis_wait(cnex_soft_state_t *ssp, cnex_intr_t *iinfo) 403*6408Sha137994 { 404*6408Sha137994 int rv, intr_state, retries; 405*6408Sha137994 406*6408Sha137994 /* disable interrupts */ 407*6408Sha137994 rv = hvldc_intr_setvalid(ssp->cfghdl, iinfo->ino, HV_INTR_NOTVALID); 408*6408Sha137994 if (rv) { 409*6408Sha137994 DWARN("cnex_intr_dis_wait: ino=0x%llx, can't set valid\n", 410*6408Sha137994 iinfo->ino); 411*6408Sha137994 return (ENXIO); 412*6408Sha137994 } 413*6408Sha137994 414*6408Sha137994 /* 415*6408Sha137994 * Make a best effort to wait for pending interrupts 416*6408Sha137994 * to finish. There is not much we can do if we timeout. 417*6408Sha137994 */ 418*6408Sha137994 retries = 0; 419*6408Sha137994 420*6408Sha137994 do { 421*6408Sha137994 rv = hvldc_intr_getstate(ssp->cfghdl, iinfo->ino, &intr_state); 422*6408Sha137994 if (rv) { 423*6408Sha137994 DWARN("cnex_intr_dis_wait: ino=0x%llx, can't get " 424*6408Sha137994 "state\n", iinfo->ino); 425*6408Sha137994 return (ENXIO); 426*6408Sha137994 } 427*6408Sha137994 428*6408Sha137994 if (intr_state != HV_INTR_DELIVERED_STATE) 429*6408Sha137994 break; 430*6408Sha137994 431*6408Sha137994 drv_usecwait(cnex_wait_usecs); 432*6408Sha137994 433*6408Sha137994 } while (!panicstr && ++retries <= cnex_wait_retries); 434*6408Sha137994 435*6408Sha137994 return (0); 436*6408Sha137994 } 437*6408Sha137994 438*6408Sha137994 /* 439*6408Sha137994 * Returns the interrupt weight to use for the specified devclass. 440*6408Sha137994 */ 441*6408Sha137994 static int32_t 442*6408Sha137994 cnex_class_weight(ldc_dev_t devclass) 443*6408Sha137994 { 444*6408Sha137994 int idx; 445*6408Sha137994 446*6408Sha137994 for (idx = 0; idx < CNEX_MAX_DEVS; idx++) { 447*6408Sha137994 if (devclass == cnex_class_to_intr[idx].devclass) { 448*6408Sha137994 return (cnex_class_to_intr[idx].weight); 449*6408Sha137994 } 450*6408Sha137994 } 451*6408Sha137994 452*6408Sha137994 /* 453*6408Sha137994 * If this code is reached, the specified devclass is 454*6408Sha137994 * invalid. New devclasses should be added to 455*6408Sha137994 * cnex_class_to_intr. 456*6408Sha137994 */ 457*6408Sha137994 ASSERT(0); 458*6408Sha137994 459*6408Sha137994 return (0); 460*6408Sha137994 } 461*6408Sha137994 462*6408Sha137994 /* 4631991Sheppo * Exported interface to register a LDC endpoint with 4641991Sheppo * the channel nexus 4651991Sheppo */ 4661991Sheppo static int 4671991Sheppo cnex_reg_chan(dev_info_t *dip, uint64_t id, ldc_dev_t devclass) 4681991Sheppo { 4691991Sheppo int idx; 4701991Sheppo cnex_ldc_t *cldcp; 4711991Sheppo int listsz, num_nodes, num_channels; 4721991Sheppo md_t *mdp = NULL; 4731991Sheppo mde_cookie_t rootnode, *listp = NULL; 4743010Slm66018 uint64_t tmp_id; 4753010Slm66018 uint64_t rxino = (uint64_t)-1; 4763010Slm66018 uint64_t txino = (uint64_t)-1; 4771991Sheppo cnex_soft_state_t *cnex_ssp; 4781991Sheppo int status, instance; 4794750Sraghuram dev_info_t *chan_dip = NULL; 4801991Sheppo 4811991Sheppo /* Get device instance and structure */ 4821991Sheppo instance = ddi_get_instance(dip); 4831991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 4841991Sheppo 4851991Sheppo /* Check to see if channel is already registered */ 4861991Sheppo mutex_enter(&cnex_ssp->clist_lock); 4871991Sheppo cldcp = cnex_ssp->clist; 4881991Sheppo while (cldcp) { 4891991Sheppo if (cldcp->id == id) { 4901991Sheppo DWARN("cnex_reg_chan: channel 0x%llx exists\n", id); 4911991Sheppo mutex_exit(&cnex_ssp->clist_lock); 4921991Sheppo return (EINVAL); 4931991Sheppo } 4941991Sheppo cldcp = cldcp->next; 4951991Sheppo } 4961991Sheppo 4971991Sheppo /* Get the Tx/Rx inos from the MD */ 4981991Sheppo if ((mdp = md_get_handle()) == NULL) { 4991991Sheppo DWARN("cnex_reg_chan: cannot init MD\n"); 5001991Sheppo mutex_exit(&cnex_ssp->clist_lock); 5011991Sheppo return (ENXIO); 5021991Sheppo } 5031991Sheppo num_nodes = md_node_count(mdp); 5041991Sheppo ASSERT(num_nodes > 0); 5051991Sheppo 5061991Sheppo listsz = num_nodes * sizeof (mde_cookie_t); 5071991Sheppo listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP); 5081991Sheppo 5091991Sheppo rootnode = md_root_node(mdp); 5101991Sheppo 5111991Sheppo /* search for all channel_endpoint nodes */ 5121991Sheppo num_channels = md_scan_dag(mdp, rootnode, 5131991Sheppo md_find_name(mdp, "channel-endpoint"), 5141991Sheppo md_find_name(mdp, "fwd"), listp); 5151991Sheppo if (num_channels <= 0) { 5161991Sheppo DWARN("cnex_reg_chan: invalid channel id\n"); 5171991Sheppo kmem_free(listp, listsz); 5181991Sheppo (void) md_fini_handle(mdp); 5191991Sheppo mutex_exit(&cnex_ssp->clist_lock); 5201991Sheppo return (EINVAL); 5211991Sheppo } 5221991Sheppo 5231991Sheppo for (idx = 0; idx < num_channels; idx++) { 5241991Sheppo 5251991Sheppo /* Get the channel ID */ 5261991Sheppo status = md_get_prop_val(mdp, listp[idx], "id", &tmp_id); 5271991Sheppo if (status) { 5281991Sheppo DWARN("cnex_reg_chan: cannot read LDC ID\n"); 5291991Sheppo kmem_free(listp, listsz); 5301991Sheppo (void) md_fini_handle(mdp); 5311991Sheppo mutex_exit(&cnex_ssp->clist_lock); 5321991Sheppo return (ENXIO); 5331991Sheppo } 5341991Sheppo if (tmp_id != id) 5351991Sheppo continue; 5361991Sheppo 5371991Sheppo /* Get the Tx and Rx ino */ 5381991Sheppo status = md_get_prop_val(mdp, listp[idx], "tx-ino", &txino); 5391991Sheppo if (status) { 5401991Sheppo DWARN("cnex_reg_chan: cannot read Tx ino\n"); 5411991Sheppo kmem_free(listp, listsz); 5421991Sheppo (void) md_fini_handle(mdp); 5431991Sheppo mutex_exit(&cnex_ssp->clist_lock); 5441991Sheppo return (ENXIO); 5451991Sheppo } 5461991Sheppo status = md_get_prop_val(mdp, listp[idx], "rx-ino", &rxino); 5471991Sheppo if (status) { 5481991Sheppo DWARN("cnex_reg_chan: cannot read Rx ino\n"); 5491991Sheppo kmem_free(listp, listsz); 5501991Sheppo (void) md_fini_handle(mdp); 5511991Sheppo mutex_exit(&cnex_ssp->clist_lock); 5521991Sheppo return (ENXIO); 5531991Sheppo } 5544750Sraghuram chan_dip = cnex_find_chan_dip(dip, id, mdp, listp[idx]); 5554750Sraghuram ASSERT(chan_dip != NULL); 5561991Sheppo } 5571991Sheppo kmem_free(listp, listsz); 5581991Sheppo (void) md_fini_handle(mdp); 5591991Sheppo 5603010Slm66018 /* 5613010Slm66018 * check to see if we looped through the list of channel IDs without 5623010Slm66018 * matching one (i.e. an 'ino' has not been initialised). 5633010Slm66018 */ 5643010Slm66018 if ((rxino == -1) || (txino == -1)) { 5653010Slm66018 DERR("cnex_reg_chan: no ID matching '%llx' in MD\n", id); 5663010Slm66018 mutex_exit(&cnex_ssp->clist_lock); 5673010Slm66018 return (ENOENT); 5683010Slm66018 } 5693010Slm66018 5701991Sheppo /* Allocate a new channel structure */ 5711991Sheppo cldcp = kmem_zalloc(sizeof (*cldcp), KM_SLEEP); 5721991Sheppo 5731991Sheppo /* Initialize the channel */ 5741991Sheppo mutex_init(&cldcp->lock, NULL, MUTEX_DRIVER, NULL); 5751991Sheppo 5761991Sheppo cldcp->id = id; 5771991Sheppo cldcp->tx.ino = txino; 5781991Sheppo cldcp->rx.ino = rxino; 5791991Sheppo cldcp->devclass = devclass; 580*6408Sha137994 cldcp->tx.weight = CNEX_TX_INTR_WEIGHT; 581*6408Sha137994 cldcp->rx.weight = cnex_class_weight(devclass); 5824750Sraghuram cldcp->dip = chan_dip; 5831991Sheppo 5841991Sheppo /* add channel to nexus channel list */ 5851991Sheppo cldcp->next = cnex_ssp->clist; 5861991Sheppo cnex_ssp->clist = cldcp; 5871991Sheppo 5881991Sheppo mutex_exit(&cnex_ssp->clist_lock); 5891991Sheppo 5901991Sheppo return (0); 5911991Sheppo } 5921991Sheppo 5931991Sheppo /* 5941991Sheppo * Add Tx/Rx interrupt handler for the channel 5951991Sheppo */ 5961991Sheppo static int 5971991Sheppo cnex_add_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype, 5981991Sheppo uint_t (*hdlr)(), caddr_t arg1, caddr_t arg2) 5991991Sheppo { 6001991Sheppo int rv, idx, pil; 6011991Sheppo cnex_ldc_t *cldcp; 6021991Sheppo cnex_intr_t *iinfo; 6031991Sheppo cnex_soft_state_t *cnex_ssp; 6041991Sheppo int instance; 6051991Sheppo 6061991Sheppo /* Get device instance and structure */ 6071991Sheppo instance = ddi_get_instance(dip); 6081991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 6091991Sheppo 6101991Sheppo /* get channel info */ 6111991Sheppo mutex_enter(&cnex_ssp->clist_lock); 6121991Sheppo cldcp = cnex_ssp->clist; 6131991Sheppo while (cldcp) { 6141991Sheppo if (cldcp->id == id) 6151991Sheppo break; 6161991Sheppo cldcp = cldcp->next; 6171991Sheppo } 6181991Sheppo if (cldcp == NULL) { 6191991Sheppo DWARN("cnex_add_intr: channel 0x%llx does not exist\n", id); 6201991Sheppo mutex_exit(&cnex_ssp->clist_lock); 6211991Sheppo return (EINVAL); 6221991Sheppo } 6231991Sheppo mutex_exit(&cnex_ssp->clist_lock); 6241991Sheppo 6251991Sheppo /* get channel lock */ 6261991Sheppo mutex_enter(&cldcp->lock); 6271991Sheppo 6281991Sheppo /* get interrupt type */ 6291991Sheppo if (itype == CNEX_TX_INTR) { 6301991Sheppo iinfo = &(cldcp->tx); 6311991Sheppo } else if (itype == CNEX_RX_INTR) { 6321991Sheppo iinfo = &(cldcp->rx); 6331991Sheppo } else { 6341991Sheppo DWARN("cnex_add_intr: invalid interrupt type\n", id); 6351991Sheppo mutex_exit(&cldcp->lock); 6361991Sheppo return (EINVAL); 6371991Sheppo } 6381991Sheppo 6391991Sheppo /* check if a handler is already added */ 6401991Sheppo if (iinfo->hdlr != 0) { 6411991Sheppo DWARN("cnex_add_intr: interrupt handler exists\n"); 6421991Sheppo mutex_exit(&cldcp->lock); 6431991Sheppo return (EINVAL); 6441991Sheppo } 6451991Sheppo 6461991Sheppo /* save interrupt handler info */ 6471991Sheppo iinfo->hdlr = hdlr; 6481991Sheppo iinfo->arg1 = arg1; 6491991Sheppo iinfo->arg2 = arg2; 6501991Sheppo 6515661Slm66018 /* save data for DTrace probes used by intrstat(1m) */ 6525661Slm66018 iinfo->dip = cldcp->dip; 6535661Slm66018 iinfo->id = cldcp->id; 6541991Sheppo 6555513Slm66018 iinfo->icookie = MINVINTR_COOKIE + iinfo->ino; 6565513Slm66018 6571991Sheppo /* 6585513Slm66018 * Verify that the ino does not generate a cookie which 6595513Slm66018 * is outside the (MINVINTR_COOKIE, MAXIVNUM) range of the 6605513Slm66018 * system interrupt table. 6611991Sheppo */ 6625513Slm66018 if (iinfo->icookie >= MAXIVNUM || iinfo->icookie < MINVINTR_COOKIE) { 6635513Slm66018 DWARN("cnex_add_intr: invalid cookie %x ino %x\n", 6645513Slm66018 iinfo->icookie, iinfo->ino); 6655513Slm66018 mutex_exit(&cldcp->lock); 6665513Slm66018 return (EINVAL); 6675513Slm66018 } 6681991Sheppo 6691991Sheppo D1("cnex_add_intr: add hdlr, cfghdl=0x%llx, ino=0x%llx, " 6701991Sheppo "cookie=0x%llx\n", cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie); 6711991Sheppo 6721991Sheppo /* Pick a PIL on the basis of the channel's devclass */ 6731991Sheppo for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) { 674*6408Sha137994 if (cldcp->devclass == cnex_class_to_intr[idx].devclass) { 675*6408Sha137994 pil = cnex_class_to_intr[idx].pil; 6761991Sheppo break; 6771991Sheppo } 6781991Sheppo } 6791991Sheppo 6801991Sheppo /* add interrupt to solaris ivec table */ 6815513Slm66018 if (add_ivintr(iinfo->icookie, pil, (intrfunc)cnex_intr_wrapper, 6825513Slm66018 (caddr_t)iinfo, NULL, NULL) != 0) { 6835513Slm66018 DWARN("cnex_add_intr: add_ivintr fail cookie %x ino %x\n", 6845513Slm66018 iinfo->icookie, iinfo->ino); 6855513Slm66018 mutex_exit(&cldcp->lock); 6865513Slm66018 return (EINVAL); 6875513Slm66018 } 6881991Sheppo 6891991Sheppo /* set the cookie in the HV */ 6901991Sheppo rv = hvldc_intr_setcookie(cnex_ssp->cfghdl, iinfo->ino, iinfo->icookie); 6911991Sheppo 6921991Sheppo /* pick next CPU in the domain for this channel */ 6934750Sraghuram iinfo->cpuid = intr_dist_cpuid(); 6941991Sheppo 6951991Sheppo /* set the target CPU and then enable interrupts */ 6964750Sraghuram rv = hvldc_intr_settarget(cnex_ssp->cfghdl, iinfo->ino, iinfo->cpuid); 6971991Sheppo if (rv) { 6981991Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set target cpu\n", 6991991Sheppo iinfo->ino); 7001991Sheppo goto hv_error; 7011991Sheppo } 7021991Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino, 7031991Sheppo HV_INTR_IDLE_STATE); 7041991Sheppo if (rv) { 7051991Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set state\n", 7061991Sheppo iinfo->ino); 7071991Sheppo goto hv_error; 7081991Sheppo } 7091991Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, iinfo->ino, HV_INTR_VALID); 7101991Sheppo if (rv) { 7111991Sheppo DWARN("cnex_add_intr: ino=0x%llx, cannot set valid\n", 7121991Sheppo iinfo->ino); 7131991Sheppo goto hv_error; 7141991Sheppo } 7151991Sheppo 716*6408Sha137994 intr_dist_cpuid_add_device_weight(iinfo->cpuid, iinfo->dip, 717*6408Sha137994 iinfo->weight); 718*6408Sha137994 7191991Sheppo mutex_exit(&cldcp->lock); 7201991Sheppo return (0); 7211991Sheppo 7221991Sheppo hv_error: 7232973Sgovinda (void) rem_ivintr(iinfo->icookie, pil); 7241991Sheppo mutex_exit(&cldcp->lock); 7251991Sheppo return (ENXIO); 7261991Sheppo } 7271991Sheppo 7281991Sheppo 7291991Sheppo /* 7301991Sheppo * Exported interface to unregister a LDC endpoint with 7311991Sheppo * the channel nexus 7321991Sheppo */ 7331991Sheppo static int 7341991Sheppo cnex_unreg_chan(dev_info_t *dip, uint64_t id) 7351991Sheppo { 7361991Sheppo cnex_ldc_t *cldcp, *prev_cldcp; 7371991Sheppo cnex_soft_state_t *cnex_ssp; 7381991Sheppo int instance; 7391991Sheppo 7401991Sheppo /* Get device instance and structure */ 7411991Sheppo instance = ddi_get_instance(dip); 7421991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 7431991Sheppo 7441991Sheppo /* find and remove channel from list */ 7451991Sheppo mutex_enter(&cnex_ssp->clist_lock); 7461991Sheppo prev_cldcp = NULL; 7471991Sheppo cldcp = cnex_ssp->clist; 7481991Sheppo while (cldcp) { 7491991Sheppo if (cldcp->id == id) 7501991Sheppo break; 7511991Sheppo prev_cldcp = cldcp; 7521991Sheppo cldcp = cldcp->next; 7531991Sheppo } 7541991Sheppo 7551991Sheppo if (cldcp == 0) { 7561991Sheppo DWARN("cnex_unreg_chan: invalid channel %d\n", id); 7571991Sheppo mutex_exit(&cnex_ssp->clist_lock); 7581991Sheppo return (EINVAL); 7591991Sheppo } 7601991Sheppo 7611991Sheppo if (cldcp->tx.hdlr || cldcp->rx.hdlr) { 7623010Slm66018 DWARN("cnex_unreg_chan: handlers still exist: chan %lx\n", id); 7631991Sheppo mutex_exit(&cnex_ssp->clist_lock); 7641991Sheppo return (ENXIO); 7651991Sheppo } 7661991Sheppo 7671991Sheppo if (prev_cldcp) 7681991Sheppo prev_cldcp->next = cldcp->next; 7691991Sheppo else 7701991Sheppo cnex_ssp->clist = cldcp->next; 7711991Sheppo 7721991Sheppo mutex_exit(&cnex_ssp->clist_lock); 7731991Sheppo 7741991Sheppo /* destroy mutex */ 7751991Sheppo mutex_destroy(&cldcp->lock); 7761991Sheppo 7771991Sheppo /* free channel */ 7781991Sheppo kmem_free(cldcp, sizeof (*cldcp)); 7791991Sheppo 7801991Sheppo return (0); 7811991Sheppo } 7821991Sheppo 7831991Sheppo /* 7841991Sheppo * Remove Tx/Rx interrupt handler for the channel 7851991Sheppo */ 7861991Sheppo static int 7871991Sheppo cnex_rem_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype) 7881991Sheppo { 7892973Sgovinda int rv, idx, pil; 7901991Sheppo cnex_ldc_t *cldcp; 7911991Sheppo cnex_intr_t *iinfo; 7921991Sheppo cnex_soft_state_t *cnex_ssp; 7931991Sheppo int instance, istate; 7941991Sheppo 7951991Sheppo /* Get device instance and structure */ 7961991Sheppo instance = ddi_get_instance(dip); 7971991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 7981991Sheppo 7991991Sheppo /* get channel info */ 8001991Sheppo mutex_enter(&cnex_ssp->clist_lock); 8011991Sheppo cldcp = cnex_ssp->clist; 8021991Sheppo while (cldcp) { 8031991Sheppo if (cldcp->id == id) 8041991Sheppo break; 8051991Sheppo cldcp = cldcp->next; 8061991Sheppo } 8071991Sheppo if (cldcp == NULL) { 8081991Sheppo DWARN("cnex_rem_intr: channel 0x%llx does not exist\n", id); 8091991Sheppo mutex_exit(&cnex_ssp->clist_lock); 8101991Sheppo return (EINVAL); 8111991Sheppo } 8121991Sheppo mutex_exit(&cnex_ssp->clist_lock); 8131991Sheppo 8141991Sheppo /* get rid of the channel intr handler */ 8151991Sheppo mutex_enter(&cldcp->lock); 8161991Sheppo 8171991Sheppo /* get interrupt type */ 8181991Sheppo if (itype == CNEX_TX_INTR) { 8191991Sheppo iinfo = &(cldcp->tx); 8201991Sheppo } else if (itype == CNEX_RX_INTR) { 8211991Sheppo iinfo = &(cldcp->rx); 8221991Sheppo } else { 8231991Sheppo DWARN("cnex_rem_intr: invalid interrupt type\n"); 8241991Sheppo mutex_exit(&cldcp->lock); 8251991Sheppo return (EINVAL); 8261991Sheppo } 8271991Sheppo 8281991Sheppo D1("cnex_rem_intr: interrupt ino=0x%x\n", iinfo->ino); 8291991Sheppo 8301991Sheppo /* check if a handler is already added */ 8311991Sheppo if (iinfo->hdlr == 0) { 8321991Sheppo DWARN("cnex_rem_intr: interrupt handler does not exist\n"); 8331991Sheppo mutex_exit(&cldcp->lock); 8341991Sheppo return (EINVAL); 8351991Sheppo } 8361991Sheppo 8371991Sheppo D1("cnex_rem_intr: set intr to invalid ino=0x%x\n", iinfo->ino); 8381991Sheppo rv = hvldc_intr_setvalid(cnex_ssp->cfghdl, 8391991Sheppo iinfo->ino, HV_INTR_NOTVALID); 8401991Sheppo if (rv) { 8411991Sheppo DWARN("cnex_rem_intr: cannot set valid ino=%x\n", iinfo->ino); 8421991Sheppo mutex_exit(&cldcp->lock); 8431991Sheppo return (ENXIO); 8441991Sheppo } 8451991Sheppo 8461991Sheppo /* 8472841Snarayan * Check if there are pending interrupts. If interrupts are 8482841Snarayan * pending return EAGAIN. 8491991Sheppo */ 8502841Snarayan rv = hvldc_intr_getstate(cnex_ssp->cfghdl, iinfo->ino, &istate); 8512841Snarayan if (rv) { 8522841Snarayan DWARN("cnex_rem_intr: ino=0x%llx, cannot get state\n", 8532841Snarayan iinfo->ino); 8542841Snarayan mutex_exit(&cldcp->lock); 8552841Snarayan return (ENXIO); 8562841Snarayan } 8571991Sheppo 8581991Sheppo /* if interrupts are still pending print warning */ 8591991Sheppo if (istate != HV_INTR_IDLE_STATE) { 8601991Sheppo DWARN("cnex_rem_intr: cannot remove intr busy ino=%x\n", 8611991Sheppo iinfo->ino); 8622336Snarayan mutex_exit(&cldcp->lock); 8632336Snarayan return (EAGAIN); 8641991Sheppo } 8651991Sheppo 8662973Sgovinda /* Pick a PIL on the basis of the channel's devclass */ 8672973Sgovinda for (idx = 0, pil = PIL_3; idx < CNEX_MAX_DEVS; idx++) { 868*6408Sha137994 if (cldcp->devclass == cnex_class_to_intr[idx].devclass) { 869*6408Sha137994 pil = cnex_class_to_intr[idx].pil; 8702973Sgovinda break; 8712973Sgovinda } 8722973Sgovinda } 8732973Sgovinda 874*6408Sha137994 intr_dist_cpuid_rem_device_weight(iinfo->cpuid, iinfo->dip); 875*6408Sha137994 8761991Sheppo /* remove interrupt */ 8772973Sgovinda (void) rem_ivintr(iinfo->icookie, pil); 8781991Sheppo 8791991Sheppo /* clear interrupt info */ 8801991Sheppo bzero(iinfo, sizeof (*iinfo)); 8811991Sheppo 8821991Sheppo mutex_exit(&cldcp->lock); 8831991Sheppo 8841991Sheppo return (0); 8851991Sheppo } 8861991Sheppo 8871991Sheppo 8881991Sheppo /* 8891991Sheppo * Clear pending Tx/Rx interrupt 8901991Sheppo */ 8911991Sheppo static int 8921991Sheppo cnex_clr_intr(dev_info_t *dip, uint64_t id, cnex_intrtype_t itype) 8931991Sheppo { 8941991Sheppo int rv; 8951991Sheppo cnex_ldc_t *cldcp; 8961991Sheppo cnex_intr_t *iinfo; 8971991Sheppo cnex_soft_state_t *cnex_ssp; 8981991Sheppo int instance; 8991991Sheppo 9001991Sheppo /* Get device instance and structure */ 9011991Sheppo instance = ddi_get_instance(dip); 9021991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 9031991Sheppo 9041991Sheppo /* get channel info */ 9051991Sheppo mutex_enter(&cnex_ssp->clist_lock); 9061991Sheppo cldcp = cnex_ssp->clist; 9071991Sheppo while (cldcp) { 9081991Sheppo if (cldcp->id == id) 9091991Sheppo break; 9101991Sheppo cldcp = cldcp->next; 9111991Sheppo } 9121991Sheppo if (cldcp == NULL) { 9131991Sheppo DWARN("cnex_clr_intr: channel 0x%llx does not exist\n", id); 9141991Sheppo mutex_exit(&cnex_ssp->clist_lock); 9151991Sheppo return (EINVAL); 9161991Sheppo } 9171991Sheppo mutex_exit(&cnex_ssp->clist_lock); 9181991Sheppo 9191991Sheppo mutex_enter(&cldcp->lock); 9201991Sheppo 9211991Sheppo /* get interrupt type */ 9221991Sheppo if (itype == CNEX_TX_INTR) { 9231991Sheppo iinfo = &(cldcp->tx); 9241991Sheppo } else if (itype == CNEX_RX_INTR) { 9251991Sheppo iinfo = &(cldcp->rx); 9261991Sheppo } else { 9272748Slm66018 DWARN("cnex_clr_intr: invalid interrupt type\n"); 9281991Sheppo mutex_exit(&cldcp->lock); 9291991Sheppo return (EINVAL); 9301991Sheppo } 9311991Sheppo 9325661Slm66018 D1("%s: interrupt ino=0x%x\n", __func__, iinfo->ino); 9331991Sheppo 9341991Sheppo /* check if a handler is already added */ 9351991Sheppo if (iinfo->hdlr == 0) { 9361991Sheppo DWARN("cnex_clr_intr: interrupt handler does not exist\n"); 9371991Sheppo mutex_exit(&cldcp->lock); 9381991Sheppo return (EINVAL); 9391991Sheppo } 9401991Sheppo 9411991Sheppo rv = hvldc_intr_setstate(cnex_ssp->cfghdl, iinfo->ino, 9421991Sheppo HV_INTR_IDLE_STATE); 9431991Sheppo if (rv) { 9442748Slm66018 DWARN("cnex_clr_intr: cannot clear interrupt state\n"); 9452336Snarayan mutex_exit(&cldcp->lock); 9462336Snarayan return (ENXIO); 9471991Sheppo } 9481991Sheppo 9491991Sheppo mutex_exit(&cldcp->lock); 9501991Sheppo 9511991Sheppo return (0); 9521991Sheppo } 9531991Sheppo 9541991Sheppo /* 9551991Sheppo * Channel nexus interrupt handler wrapper 9561991Sheppo */ 9571991Sheppo static uint_t 9581991Sheppo cnex_intr_wrapper(caddr_t arg) 9591991Sheppo { 9601991Sheppo int res; 9611991Sheppo uint_t (*handler)(); 9621991Sheppo caddr_t handler_arg1; 9631991Sheppo caddr_t handler_arg2; 9641991Sheppo cnex_intr_t *iinfo = (cnex_intr_t *)arg; 9651991Sheppo 9661991Sheppo ASSERT(iinfo != NULL); 9671991Sheppo 9681991Sheppo handler = iinfo->hdlr; 9691991Sheppo handler_arg1 = iinfo->arg1; 9701991Sheppo handler_arg2 = iinfo->arg2; 9711991Sheppo 9724750Sraghuram /* 9734750Sraghuram * The 'interrupt__start' and 'interrupt__complete' probes 9744750Sraghuram * are provided to support 'intrstat' command. These probes 9754750Sraghuram * help monitor the interrupts on a per device basis only. 9764750Sraghuram * In order to provide the ability to monitor the 9774750Sraghuram * activity on a per channel basis, two additional 9784750Sraghuram * probes('channelintr__start','channelintr__complete') 9794750Sraghuram * are provided here. 9804750Sraghuram */ 9815661Slm66018 DTRACE_PROBE4(channelintr__start, uint64_t, iinfo->id, 9824750Sraghuram cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1); 9834750Sraghuram 9845661Slm66018 DTRACE_PROBE4(interrupt__start, dev_info_t, iinfo->dip, 9854750Sraghuram void *, handler, caddr_t, handler_arg1, caddr_t, handler_arg2); 9864750Sraghuram 9874750Sraghuram D1("cnex_intr_wrapper:ino=0x%llx invoke client handler\n", iinfo->ino); 9881991Sheppo res = (*handler)(handler_arg1, handler_arg2); 9891991Sheppo 9905661Slm66018 DTRACE_PROBE4(interrupt__complete, dev_info_t, iinfo->dip, 9914750Sraghuram void *, handler, caddr_t, handler_arg1, int, res); 9924750Sraghuram 9935661Slm66018 DTRACE_PROBE4(channelintr__complete, uint64_t, iinfo->id, 9944750Sraghuram cnex_intr_t *, iinfo, void *, handler, caddr_t, handler_arg1); 9954750Sraghuram 9961991Sheppo return (res); 9971991Sheppo } 9981991Sheppo 9991991Sheppo /*ARGSUSED*/ 10001991Sheppo static int 10011991Sheppo cnex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 10021991Sheppo { 10031991Sheppo int rv, instance, reglen; 10041991Sheppo cnex_regspec_t *reg_p; 10051991Sheppo ldc_cnex_t cinfo; 10061991Sheppo cnex_soft_state_t *cnex_ssp; 10071991Sheppo 10081991Sheppo switch (cmd) { 10091991Sheppo case DDI_ATTACH: 10101991Sheppo break; 10111991Sheppo case DDI_RESUME: 10121991Sheppo return (DDI_SUCCESS); 10131991Sheppo default: 10141991Sheppo return (DDI_FAILURE); 10151991Sheppo } 10161991Sheppo 10171991Sheppo /* 10181991Sheppo * Get the instance specific soft state structure. 10191991Sheppo * Save the devi for this instance in the soft_state data. 10201991Sheppo */ 10211991Sheppo instance = ddi_get_instance(devi); 10221991Sheppo if (ddi_soft_state_zalloc(cnex_state, instance) != DDI_SUCCESS) 10231991Sheppo return (DDI_FAILURE); 10241991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 10251991Sheppo 10261991Sheppo cnex_ssp->devi = devi; 10271991Sheppo cnex_ssp->clist = NULL; 10281991Sheppo 10291991Sheppo if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 10304750Sraghuram "reg", (caddr_t)®_p, ®len) != DDI_SUCCESS) { 10311991Sheppo return (DDI_FAILURE); 10321991Sheppo } 10331991Sheppo 10341991Sheppo /* get the sun4v config handle for this device */ 10351991Sheppo cnex_ssp->cfghdl = SUN4V_REG_SPEC2CFG_HDL(reg_p->physaddr); 10361991Sheppo kmem_free(reg_p, reglen); 10371991Sheppo 10381991Sheppo D1("cnex_attach: cfghdl=0x%llx\n", cnex_ssp->cfghdl); 10391991Sheppo 10401991Sheppo /* init channel list mutex */ 10411991Sheppo mutex_init(&cnex_ssp->clist_lock, NULL, MUTEX_DRIVER, NULL); 10421991Sheppo 10431991Sheppo /* Register with LDC module */ 10441991Sheppo cinfo.dip = devi; 10451991Sheppo cinfo.reg_chan = cnex_reg_chan; 10461991Sheppo cinfo.unreg_chan = cnex_unreg_chan; 10471991Sheppo cinfo.add_intr = cnex_add_intr; 10481991Sheppo cinfo.rem_intr = cnex_rem_intr; 10491991Sheppo cinfo.clr_intr = cnex_clr_intr; 10501991Sheppo 10511991Sheppo /* 10521991Sheppo * LDC register will fail if an nexus instance had already 10531991Sheppo * registered with the LDC framework 10541991Sheppo */ 10551991Sheppo rv = ldc_register(&cinfo); 10561991Sheppo if (rv) { 10571991Sheppo DWARN("cnex_attach: unable to register with LDC\n"); 10581991Sheppo ddi_soft_state_free(cnex_state, instance); 10591991Sheppo mutex_destroy(&cnex_ssp->clist_lock); 10601991Sheppo return (DDI_FAILURE); 10611991Sheppo } 10621991Sheppo 10631991Sheppo if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance, 10641991Sheppo DDI_NT_NEXUS, 0) != DDI_SUCCESS) { 10651991Sheppo ddi_remove_minor_node(devi, NULL); 10661991Sheppo ddi_soft_state_free(cnex_state, instance); 10671991Sheppo mutex_destroy(&cnex_ssp->clist_lock); 10681991Sheppo return (DDI_FAILURE); 10691991Sheppo } 10701991Sheppo 10711991Sheppo /* Add interrupt redistribution callback. */ 1072*6408Sha137994 intr_dist_add_weighted(cnex_intr_redist, cnex_ssp); 10731991Sheppo 10741991Sheppo ddi_report_dev(devi); 10751991Sheppo return (DDI_SUCCESS); 10761991Sheppo } 10771991Sheppo 10781991Sheppo /*ARGSUSED*/ 10791991Sheppo static int 10801991Sheppo cnex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 10811991Sheppo { 10821991Sheppo int instance; 10831991Sheppo ldc_cnex_t cinfo; 10841991Sheppo cnex_soft_state_t *cnex_ssp; 10851991Sheppo 10861991Sheppo switch (cmd) { 10871991Sheppo case DDI_DETACH: 10881991Sheppo break; 10891991Sheppo case DDI_SUSPEND: 10901991Sheppo return (DDI_SUCCESS); 10911991Sheppo default: 10921991Sheppo return (DDI_FAILURE); 10931991Sheppo } 10941991Sheppo 10951991Sheppo instance = ddi_get_instance(devi); 10961991Sheppo cnex_ssp = ddi_get_soft_state(cnex_state, instance); 10971991Sheppo 10981991Sheppo /* check if there are any channels still registered */ 10991991Sheppo if (cnex_ssp->clist) { 11001991Sheppo cmn_err(CE_WARN, "?cnex_dettach: channels registered %d\n", 11011991Sheppo ddi_get_instance(devi)); 11021991Sheppo return (DDI_FAILURE); 11031991Sheppo } 11041991Sheppo 11051991Sheppo /* Unregister with LDC module */ 11061991Sheppo cinfo.dip = devi; 11071991Sheppo (void) ldc_unregister(&cinfo); 11081991Sheppo 11091991Sheppo /* Remove interrupt redistribution callback. */ 1110*6408Sha137994 intr_dist_rem_weighted(cnex_intr_redist, cnex_ssp); 11111991Sheppo 11121991Sheppo /* destroy mutex */ 11131991Sheppo mutex_destroy(&cnex_ssp->clist_lock); 11141991Sheppo 11151991Sheppo /* free soft state structure */ 11161991Sheppo ddi_soft_state_free(cnex_state, instance); 11171991Sheppo 11181991Sheppo return (DDI_SUCCESS); 11191991Sheppo } 11201991Sheppo 11211991Sheppo /*ARGSUSED*/ 11221991Sheppo static int 11231991Sheppo cnex_open(dev_t *devp, int flags, int otyp, cred_t *credp) 11241991Sheppo { 11251991Sheppo int instance; 11261991Sheppo 11271991Sheppo if (otyp != OTYP_CHR) 11281991Sheppo return (EINVAL); 11291991Sheppo 11301991Sheppo instance = getminor(*devp); 11311991Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL) 11321991Sheppo return (ENXIO); 11331991Sheppo 11341991Sheppo return (0); 11351991Sheppo } 11361991Sheppo 11371991Sheppo /*ARGSUSED*/ 11381991Sheppo static int 11391991Sheppo cnex_close(dev_t dev, int flags, int otyp, cred_t *credp) 11401991Sheppo { 11411991Sheppo int instance; 11421991Sheppo 11431991Sheppo if (otyp != OTYP_CHR) 11441991Sheppo return (EINVAL); 11451991Sheppo 11461991Sheppo instance = getminor(dev); 11471991Sheppo if (ddi_get_soft_state(cnex_state, instance) == NULL) 11481991Sheppo return (ENXIO); 11491991Sheppo 11501991Sheppo return (0); 11511991Sheppo } 11521991Sheppo 11531991Sheppo /*ARGSUSED*/ 11541991Sheppo static int 11551991Sheppo cnex_ioctl(dev_t dev, 11561991Sheppo int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p) 11571991Sheppo { 11581991Sheppo int instance; 11591991Sheppo cnex_soft_state_t *cnex_ssp; 11601991Sheppo 11611991Sheppo instance = getminor(dev); 11621991Sheppo if ((cnex_ssp = ddi_get_soft_state(cnex_state, instance)) == NULL) 11631991Sheppo return (ENXIO); 11641991Sheppo ASSERT(cnex_ssp->devi); 11651991Sheppo return (ndi_devctl_ioctl(cnex_ssp->devi, cmd, arg, mode, 0)); 11661991Sheppo } 11671991Sheppo 11681991Sheppo static int 11691991Sheppo cnex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 11701991Sheppo void *arg, void *result) 11711991Sheppo { 11721991Sheppo char name[MAXNAMELEN]; 11731991Sheppo uint32_t reglen; 11741991Sheppo int *cnex_regspec; 11751991Sheppo 11761991Sheppo switch (ctlop) { 11771991Sheppo case DDI_CTLOPS_REPORTDEV: 11781991Sheppo if (rdip == NULL) 11791991Sheppo return (DDI_FAILURE); 11801991Sheppo cmn_err(CE_CONT, "?channel-device: %s%d\n", 11811991Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip)); 11821991Sheppo return (DDI_SUCCESS); 11831991Sheppo 11841991Sheppo case DDI_CTLOPS_INITCHILD: 11851991Sheppo { 11861991Sheppo dev_info_t *child = (dev_info_t *)arg; 11871991Sheppo 11881991Sheppo if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 11894750Sraghuram DDI_PROP_DONTPASS, "reg", 11904750Sraghuram &cnex_regspec, ®len) != DDI_SUCCESS) { 11911991Sheppo return (DDI_FAILURE); 11921991Sheppo } 11931991Sheppo 11941991Sheppo (void) snprintf(name, sizeof (name), "%x", *cnex_regspec); 11951991Sheppo ddi_set_name_addr(child, name); 11961991Sheppo ddi_set_parent_data(child, NULL); 11971991Sheppo ddi_prop_free(cnex_regspec); 11981991Sheppo return (DDI_SUCCESS); 11991991Sheppo } 12001991Sheppo 12011991Sheppo case DDI_CTLOPS_UNINITCHILD: 12021991Sheppo { 12031991Sheppo dev_info_t *child = (dev_info_t *)arg; 12041991Sheppo 12051991Sheppo NDI_CONFIG_DEBUG((CE_NOTE, 12061991Sheppo "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)", 12071991Sheppo ddi_driver_name(child), DEVI(child)->devi_instance)); 12081991Sheppo 12091991Sheppo ddi_set_name_addr(child, NULL); 12101991Sheppo 12111991Sheppo return (DDI_SUCCESS); 12121991Sheppo } 12131991Sheppo 12141991Sheppo case DDI_CTLOPS_DMAPMAPC: 12151991Sheppo case DDI_CTLOPS_REPORTINT: 12161991Sheppo case DDI_CTLOPS_REGSIZE: 12171991Sheppo case DDI_CTLOPS_NREGS: 12181991Sheppo case DDI_CTLOPS_SIDDEV: 12191991Sheppo case DDI_CTLOPS_SLAVEONLY: 12201991Sheppo case DDI_CTLOPS_AFFINITY: 12211991Sheppo case DDI_CTLOPS_POKE: 12221991Sheppo case DDI_CTLOPS_PEEK: 12231991Sheppo /* 12241991Sheppo * These ops correspond to functions that "shouldn't" be called 12251991Sheppo * by a channel-device driver. So we whine when we're called. 12261991Sheppo */ 12271991Sheppo cmn_err(CE_WARN, "%s%d: invalid op (%d) from %s%d\n", 12281991Sheppo ddi_driver_name(dip), ddi_get_instance(dip), ctlop, 12291991Sheppo ddi_driver_name(rdip), ddi_get_instance(rdip)); 12301991Sheppo return (DDI_FAILURE); 12311991Sheppo 12321991Sheppo case DDI_CTLOPS_ATTACH: 12331991Sheppo case DDI_CTLOPS_BTOP: 12341991Sheppo case DDI_CTLOPS_BTOPR: 12351991Sheppo case DDI_CTLOPS_DETACH: 12361991Sheppo case DDI_CTLOPS_DVMAPAGESIZE: 12371991Sheppo case DDI_CTLOPS_IOMIN: 12381991Sheppo case DDI_CTLOPS_POWER: 12391991Sheppo case DDI_CTLOPS_PTOB: 12401991Sheppo default: 12411991Sheppo /* 12421991Sheppo * Everything else (e.g. PTOB/BTOP/BTOPR requests) we pass up 12431991Sheppo */ 12441991Sheppo return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 12451991Sheppo } 12461991Sheppo } 12471991Sheppo 12484750Sraghuram /* 12494750Sraghuram * cnex_find_chan_dip -- Find the dip of a device that is corresponding 12504750Sraghuram * to the specific channel. Below are the details on how the dip 12514750Sraghuram * is derived. 12524750Sraghuram * 12534750Sraghuram * - In the MD, the cfg-handle is expected to be unique for 12544750Sraghuram * virtual-device nodes that have the same 'name' property value. 12554750Sraghuram * This value is expected to be the same as that of "reg" property 12564750Sraghuram * of the corresponding OBP device node. 12574750Sraghuram * 12584750Sraghuram * - The value of the 'name' property of a virtual-device node 12594750Sraghuram * in the MD is expected to be the same for the corresponding 12604750Sraghuram * OBP device node. 12614750Sraghuram * 12624750Sraghuram * - Find the virtual-device node corresponding to a channel-endpoint 12634750Sraghuram * by walking backwards. Then obtain the values for the 'name' and 12644750Sraghuram * 'cfg-handle' properties. 12654750Sraghuram * 12664750Sraghuram * - Walk all the children of the cnex, find a matching dip which 12674750Sraghuram * has the same 'name' and 'reg' property values. 12684750Sraghuram * 12694750Sraghuram * - The channels that have no corresponding device driver are 12704750Sraghuram * treated as if they correspond to the cnex driver, 12714750Sraghuram * that is, return cnex dip for them. This means, the 12724750Sraghuram * cnex acts as an umbrella device driver. Note, this is 12734750Sraghuram * for 'intrstat' statistics purposes only. As a result of this, 12744750Sraghuram * the 'intrstat' shows cnex as the device that is servicing the 12754750Sraghuram * interrupts corresponding to these channels. 12764750Sraghuram * 12774750Sraghuram * For now, only one such case is known, that is, the channels that 12784750Sraghuram * are used by the "domain-services". 12794750Sraghuram */ 12804750Sraghuram static dev_info_t * 12814750Sraghuram cnex_find_chan_dip(dev_info_t *dip, uint64_t chan_id, 12824750Sraghuram md_t *mdp, mde_cookie_t mde) 12834750Sraghuram { 12844750Sraghuram int listsz; 12854750Sraghuram int num_nodes; 12864750Sraghuram int num_devs; 12874750Sraghuram uint64_t cfghdl; 12884750Sraghuram char *md_name; 12894750Sraghuram mde_cookie_t *listp; 12904750Sraghuram dev_info_t *cdip = NULL; 12914750Sraghuram 12924750Sraghuram num_nodes = md_node_count(mdp); 12934750Sraghuram ASSERT(num_nodes > 0); 12944750Sraghuram listsz = num_nodes * sizeof (mde_cookie_t); 12954750Sraghuram listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP); 12964750Sraghuram 12974750Sraghuram num_devs = md_scan_dag(mdp, mde, md_find_name(mdp, "virtual-device"), 12984750Sraghuram md_find_name(mdp, "back"), listp); 12994750Sraghuram ASSERT(num_devs <= 1); 13004750Sraghuram if (num_devs <= 0) { 13014750Sraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): " 13024750Sraghuram "No virtual-device found\n", chan_id); 13034750Sraghuram goto fdip_exit; 13044750Sraghuram } 13054750Sraghuram if (md_get_prop_str(mdp, listp[0], "name", &md_name) != 0) { 13064750Sraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): " 13074750Sraghuram "name property not found\n", chan_id); 13084750Sraghuram goto fdip_exit; 13094750Sraghuram } 13104750Sraghuram 13114750Sraghuram D1("cnex_find_chan_dip: channel(0x%llx): virtual-device " 13124750Sraghuram "name property value = %s\n", chan_id, md_name); 13134750Sraghuram 13144750Sraghuram if (md_get_prop_val(mdp, listp[0], "cfg-handle", &cfghdl) != 0) { 13154750Sraghuram DWARN("cnex_find_chan_dip:channel(0x%llx): virtual-device's " 13164750Sraghuram "cfg-handle property not found\n", chan_id); 13174750Sraghuram goto fdip_exit; 13184750Sraghuram } 13194750Sraghuram 13204750Sraghuram D1("cnex_find_chan_dip:channel(0x%llx): virtual-device cfg-handle " 13214750Sraghuram " property value = 0x%x\n", chan_id, cfghdl); 13224750Sraghuram 13234750Sraghuram for (cdip = ddi_get_child(dip); cdip != NULL; 13244750Sraghuram cdip = ddi_get_next_sibling(cdip)) { 13254750Sraghuram 13264750Sraghuram int *cnex_regspec; 13274750Sraghuram uint32_t reglen; 13284750Sraghuram char *dev_name; 13294750Sraghuram 13304750Sraghuram if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip, 13314750Sraghuram DDI_PROP_DONTPASS, "name", 13324750Sraghuram &dev_name) != DDI_PROP_SUCCESS) { 13334750Sraghuram DWARN("cnex_find_chan_dip: name property not" 13344750Sraghuram " found for dip(0x%p)\n", cdip); 13354750Sraghuram continue; 13364750Sraghuram } 13374750Sraghuram if (strcmp(md_name, dev_name) != 0) { 13384750Sraghuram ddi_prop_free(dev_name); 13394750Sraghuram continue; 13404750Sraghuram } 13414750Sraghuram ddi_prop_free(dev_name); 13424750Sraghuram if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 13434750Sraghuram DDI_PROP_DONTPASS, "reg", 13444750Sraghuram &cnex_regspec, ®len) != DDI_SUCCESS) { 13454750Sraghuram DWARN("cnex_find_chan_dip: reg property not" 13464750Sraghuram " found for dip(0x%p)\n", cdip); 13474750Sraghuram continue; 13484750Sraghuram } 13494750Sraghuram if (*cnex_regspec == cfghdl) { 13504750Sraghuram D1("cnex_find_chan_dip:channel(0x%llx): found " 13514750Sraghuram "dip(0x%p) drvname=%s\n", chan_id, cdip, 13524750Sraghuram ddi_driver_name(cdip)); 13535729Sarutz ddi_prop_free(cnex_regspec); 13544750Sraghuram break; 13554750Sraghuram } 13564750Sraghuram ddi_prop_free(cnex_regspec); 13574750Sraghuram } 13584750Sraghuram 13594750Sraghuram fdip_exit: 13604750Sraghuram if (cdip == NULL) { 13614750Sraghuram /* 13624750Sraghuram * If a virtual-device node exists but no dip found, 13634750Sraghuram * then for now print a DEBUG error message only. 13644750Sraghuram */ 13654750Sraghuram if (num_devs > 0) { 13664750Sraghuram DERR("cnex_find_chan_dip:channel(0x%llx): " 13674750Sraghuram "No device found\n", chan_id); 13684750Sraghuram } 13694750Sraghuram 13704750Sraghuram /* If no dip was found, return cnex device's dip. */ 13714750Sraghuram cdip = dip; 13724750Sraghuram } 13734750Sraghuram 13744750Sraghuram kmem_free(listp, listsz); 13754750Sraghuram D1("cnex_find_chan_dip:channel(0x%llx): returning dip=0x%p\n", 13764750Sraghuram chan_id, cdip); 13774750Sraghuram return (cdip); 13784750Sraghuram } 13794750Sraghuram 13801991Sheppo /* -------------------------------------------------------------------------- */ 1381