1*25cf1a30Sjl /* 2*25cf1a30Sjl * CDDL HEADER START 3*25cf1a30Sjl * 4*25cf1a30Sjl * The contents of this file are subject to the terms of the 5*25cf1a30Sjl * Common Development and Distribution License (the "License"). 6*25cf1a30Sjl * You may not use this file except in compliance with the License. 7*25cf1a30Sjl * 8*25cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*25cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 10*25cf1a30Sjl * See the License for the specific language governing permissions 11*25cf1a30Sjl * and limitations under the License. 12*25cf1a30Sjl * 13*25cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 14*25cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*25cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 16*25cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 17*25cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 18*25cf1a30Sjl * 19*25cf1a30Sjl * CDDL HEADER END 20*25cf1a30Sjl */ 21*25cf1a30Sjl /* 22*25cf1a30Sjl * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*25cf1a30Sjl * Use is subject to license terms. 24*25cf1a30Sjl */ 25*25cf1a30Sjl 26*25cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI" 27*25cf1a30Sjl 28*25cf1a30Sjl /* 29*25cf1a30Sjl * CMU-CH nexus interrupt handling: 30*25cf1a30Sjl * PCI device interrupt handler wrapper 31*25cf1a30Sjl * pil lookup routine 32*25cf1a30Sjl * PCI device interrupt related initchild code 33*25cf1a30Sjl */ 34*25cf1a30Sjl 35*25cf1a30Sjl #include <sys/types.h> 36*25cf1a30Sjl #include <sys/kmem.h> 37*25cf1a30Sjl #include <sys/async.h> 38*25cf1a30Sjl #include <sys/spl.h> 39*25cf1a30Sjl #include <sys/sunddi.h> 40*25cf1a30Sjl #include <sys/machsystm.h> 41*25cf1a30Sjl #include <sys/ddi_impldefs.h> 42*25cf1a30Sjl #include <sys/pcicmu/pcicmu.h> 43*25cf1a30Sjl #include <sys/sdt.h> 44*25cf1a30Sjl 45*25cf1a30Sjl uint_t pcmu_intr_wrapper(caddr_t arg); 46*25cf1a30Sjl 47*25cf1a30Sjl /* 48*25cf1a30Sjl * interrupt jabber: 49*25cf1a30Sjl * 50*25cf1a30Sjl * When an interrupt line is jabbering, every time the state machine for the 51*25cf1a30Sjl * associated ino is idled, a new mondo will be sent and the ino will go into 52*25cf1a30Sjl * the pending state again. The mondo will cause a new call to 53*25cf1a30Sjl * pcmu_intr_wrapper() which normally idles the ino's state machine which would 54*25cf1a30Sjl * precipitate another trip round the loop. 55*25cf1a30Sjl * The loop can be broken by preventing the ino's state machine from being 56*25cf1a30Sjl * idled when an interrupt line is jabbering. See the comment at the 57*25cf1a30Sjl * beginning of pcmu_intr_wrapper() explaining how the 'interrupt jabber 58*25cf1a30Sjl * protection' code does this. 59*25cf1a30Sjl */ 60*25cf1a30Sjl 61*25cf1a30Sjl 62*25cf1a30Sjl /* 63*25cf1a30Sjl * If the unclaimed interrupt count has reached the limit set by 64*25cf1a30Sjl * pcmu_unclaimed_intr_max within the time limit, then all interrupts 65*25cf1a30Sjl * on this ino is blocked by not idling the interrupt state machine. 66*25cf1a30Sjl */ 67*25cf1a30Sjl static int 68*25cf1a30Sjl pcmu_spurintr(pcmu_ib_ino_info_t *ino_p) { 69*25cf1a30Sjl int i; 70*25cf1a30Sjl ih_t *ih_p = ino_p->pino_ih_start; 71*25cf1a30Sjl pcmu_t *pcmu_p = ino_p->pino_ib_p->pib_pcmu_p; 72*25cf1a30Sjl char *err_fmt_str; 73*25cf1a30Sjl 74*25cf1a30Sjl if (ino_p->pino_unclaimed > pcmu_unclaimed_intr_max) { 75*25cf1a30Sjl return (DDI_INTR_CLAIMED); 76*25cf1a30Sjl } 77*25cf1a30Sjl if (!ino_p->pino_unclaimed) { 78*25cf1a30Sjl ino_p->pino_spurintr_begin = ddi_get_lbolt(); 79*25cf1a30Sjl } 80*25cf1a30Sjl ino_p->pino_unclaimed++; 81*25cf1a30Sjl if (ino_p->pino_unclaimed <= pcmu_unclaimed_intr_max) { 82*25cf1a30Sjl goto clear; 83*25cf1a30Sjl } 84*25cf1a30Sjl if (drv_hztousec(ddi_get_lbolt() - ino_p->pino_spurintr_begin) 85*25cf1a30Sjl > pcmu_spurintr_duration) { 86*25cf1a30Sjl ino_p->pino_unclaimed = 0; 87*25cf1a30Sjl goto clear; 88*25cf1a30Sjl } 89*25cf1a30Sjl err_fmt_str = "%s%d: ino 0x%x blocked"; 90*25cf1a30Sjl goto warn; 91*25cf1a30Sjl clear: 92*25cf1a30Sjl /* clear the pending state */ 93*25cf1a30Sjl PCMU_IB_INO_INTR_CLEAR(ino_p->pino_clr_reg); 94*25cf1a30Sjl err_fmt_str = "!%s%d: spurious interrupt from ino 0x%x"; 95*25cf1a30Sjl warn: 96*25cf1a30Sjl cmn_err(CE_WARN, err_fmt_str, NAMEINST(pcmu_p->pcmu_dip), 97*25cf1a30Sjl ino_p->pino_ino); 98*25cf1a30Sjl for (i = 0; i < ino_p->pino_ih_size; i++, ih_p = ih_p->ih_next) { 99*25cf1a30Sjl cmn_err(CE_CONT, "!%s-%d#%x ", NAMEINST(ih_p->ih_dip), 100*25cf1a30Sjl ih_p->ih_inum); 101*25cf1a30Sjl } 102*25cf1a30Sjl cmn_err(CE_CONT, "!\n"); 103*25cf1a30Sjl return (DDI_INTR_CLAIMED); 104*25cf1a30Sjl } 105*25cf1a30Sjl 106*25cf1a30Sjl /* 107*25cf1a30Sjl * pcmu_intr_wrapper 108*25cf1a30Sjl * 109*25cf1a30Sjl * This routine is used as wrapper around interrupt handlers installed by child 110*25cf1a30Sjl * device drivers. This routine invokes the driver interrupt handlers and 111*25cf1a30Sjl * examines the return codes. 112*25cf1a30Sjl * There is a count of unclaimed interrupts kept on a per-ino basis. If at 113*25cf1a30Sjl * least one handler claims the interrupt then the counter is halved and the 114*25cf1a30Sjl * interrupt state machine is idled. If no handler claims the interrupt then 115*25cf1a30Sjl * the counter is incremented by one and the state machine is idled. 116*25cf1a30Sjl * If the count ever reaches the limit value set by pcmu_unclaimed_intr_max 117*25cf1a30Sjl * then the interrupt state machine is not idled thus preventing any further 118*25cf1a30Sjl * interrupts on that ino. The state machine will only be idled again if a 119*25cf1a30Sjl * handler is subsequently added or removed. 120*25cf1a30Sjl * 121*25cf1a30Sjl * return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt, 122*25cf1a30Sjl * DDI_INTR_UNCLAIMED otherwise. 123*25cf1a30Sjl */ 124*25cf1a30Sjl uint_t 125*25cf1a30Sjl pcmu_intr_wrapper(caddr_t arg) 126*25cf1a30Sjl { 127*25cf1a30Sjl pcmu_ib_ino_info_t *ino_p = (pcmu_ib_ino_info_t *)arg; 128*25cf1a30Sjl uint_t result = 0, r; 129*25cf1a30Sjl ih_t *ih_p = ino_p->pino_ih_start; 130*25cf1a30Sjl int i; 131*25cf1a30Sjl #ifdef DEBUG 132*25cf1a30Sjl pcmu_t *pcmu_p = ino_p->pino_ib_p->pib_pcmu_p; 133*25cf1a30Sjl #endif 134*25cf1a30Sjl 135*25cf1a30Sjl 136*25cf1a30Sjl for (i = 0; i < ino_p->pino_ih_size; i++, ih_p = ih_p->ih_next) { 137*25cf1a30Sjl dev_info_t *dip = ih_p->ih_dip; 138*25cf1a30Sjl uint_t (*handler)() = ih_p->ih_handler; 139*25cf1a30Sjl caddr_t arg1 = ih_p->ih_handler_arg1; 140*25cf1a30Sjl caddr_t arg2 = ih_p->ih_handler_arg2; 141*25cf1a30Sjl 142*25cf1a30Sjl if (ih_p->ih_intr_state == PCMU_INTR_STATE_DISABLE) { 143*25cf1a30Sjl PCMU_DBG3(PCMU_DBG_INTR, pcmu_p->pcmu_dip, 144*25cf1a30Sjl "pcmu_intr_wrapper: %s%d interrupt %d is " 145*25cf1a30Sjl "disabled\n", ddi_driver_name(dip), 146*25cf1a30Sjl ddi_get_instance(dip), ino_p->pino_ino); 147*25cf1a30Sjl continue; 148*25cf1a30Sjl } 149*25cf1a30Sjl 150*25cf1a30Sjl DTRACE_PROBE4(pcmu__interrupt__start, dev_info_t, dip, 151*25cf1a30Sjl void *, handler, caddr_t, arg1, caddr_t, arg2); 152*25cf1a30Sjl 153*25cf1a30Sjl r = (*handler)(arg1, arg2); 154*25cf1a30Sjl DTRACE_PROBE4(pcmu__interrupt__complete, dev_info_t, dip, 155*25cf1a30Sjl void *, handler, caddr_t, arg1, int, r); 156*25cf1a30Sjl 157*25cf1a30Sjl result += r; 158*25cf1a30Sjl } 159*25cf1a30Sjl 160*25cf1a30Sjl if (!result) { 161*25cf1a30Sjl return (pcmu_spurintr(ino_p)); 162*25cf1a30Sjl } 163*25cf1a30Sjl ino_p->pino_unclaimed = 0; 164*25cf1a30Sjl /* clear the pending state */ 165*25cf1a30Sjl PCMU_IB_INO_INTR_CLEAR(ino_p->pino_clr_reg); 166*25cf1a30Sjl return (DDI_INTR_CLAIMED); 167*25cf1a30Sjl } 168*25cf1a30Sjl 169*25cf1a30Sjl int 170*25cf1a30Sjl pcmu_add_intr(dev_info_t *dip, dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp) 171*25cf1a30Sjl { 172*25cf1a30Sjl pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip)); 173*25cf1a30Sjl pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p; 174*25cf1a30Sjl ih_t *ih_p; 175*25cf1a30Sjl pcmu_ib_ino_t ino; 176*25cf1a30Sjl pcmu_ib_ino_info_t *ino_p; /* pulse interrupts have no ino */ 177*25cf1a30Sjl pcmu_ib_mondo_t mondo; 178*25cf1a30Sjl uint32_t cpu_id; 179*25cf1a30Sjl int ret; 180*25cf1a30Sjl 181*25cf1a30Sjl ino = PCMU_IB_MONDO_TO_INO(hdlp->ih_vector); 182*25cf1a30Sjl 183*25cf1a30Sjl PCMU_DBG3(PCMU_DBG_A_INTX, dip, "pcmu_add_intr: rdip=%s%d ino=%x\n", 184*25cf1a30Sjl ddi_driver_name(rdip), ddi_get_instance(rdip), ino); 185*25cf1a30Sjl 186*25cf1a30Sjl if (ino > pib_p->pib_max_ino) { 187*25cf1a30Sjl PCMU_DBG1(PCMU_DBG_A_INTX, dip, "ino %x is invalid\n", ino); 188*25cf1a30Sjl return (DDI_INTR_NOTFOUND); 189*25cf1a30Sjl } 190*25cf1a30Sjl 191*25cf1a30Sjl if ((mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p, ino)) == 0) 192*25cf1a30Sjl goto fail1; 193*25cf1a30Sjl 194*25cf1a30Sjl ino = PCMU_IB_MONDO_TO_INO(mondo); 195*25cf1a30Sjl 196*25cf1a30Sjl mutex_enter(&pib_p->pib_ino_lst_mutex); 197*25cf1a30Sjl ih_p = pcmu_ib_alloc_ih(rdip, hdlp->ih_inum, 198*25cf1a30Sjl hdlp->ih_cb_func, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); 199*25cf1a30Sjl 200*25cf1a30Sjl if (ino_p = pcmu_ib_locate_ino(pib_p, ino)) { /* sharing ino */ 201*25cf1a30Sjl uint32_t intr_index = hdlp->ih_inum; 202*25cf1a30Sjl if (pcmu_ib_ino_locate_intr(ino_p, rdip, intr_index)) { 203*25cf1a30Sjl PCMU_DBG1(PCMU_DBG_A_INTX, dip, 204*25cf1a30Sjl "dup intr #%d\n", intr_index); 205*25cf1a30Sjl goto fail3; 206*25cf1a30Sjl } 207*25cf1a30Sjl 208*25cf1a30Sjl /* 209*25cf1a30Sjl * add default weight(0) to the cpu that we are 210*25cf1a30Sjl * already targeting 211*25cf1a30Sjl */ 212*25cf1a30Sjl cpu_id = ino_p->pino_cpuid; 213*25cf1a30Sjl intr_dist_cpuid_add_device_weight(cpu_id, rdip, 0); 214*25cf1a30Sjl pcmu_ib_ino_add_intr(pcmu_p, ino_p, ih_p); 215*25cf1a30Sjl goto ino_done; 216*25cf1a30Sjl } 217*25cf1a30Sjl 218*25cf1a30Sjl ino_p = pcmu_ib_new_ino(pib_p, ino, ih_p); 219*25cf1a30Sjl hdlp->ih_vector = mondo; 220*25cf1a30Sjl 221*25cf1a30Sjl PCMU_DBG2(PCMU_DBG_A_INTX, dip, "pcmu_add_intr: pil=0x%x mondo=0x%x\n", 222*25cf1a30Sjl hdlp->ih_pri, hdlp->ih_vector); 223*25cf1a30Sjl 224*25cf1a30Sjl DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 225*25cf1a30Sjl (ddi_intr_handler_t *)pcmu_intr_wrapper, (caddr_t)ino_p, NULL); 226*25cf1a30Sjl 227*25cf1a30Sjl ret = i_ddi_add_ivintr(hdlp); 228*25cf1a30Sjl 229*25cf1a30Sjl /* 230*25cf1a30Sjl * Restore original interrupt handler 231*25cf1a30Sjl * and arguments in interrupt handle. 232*25cf1a30Sjl */ 233*25cf1a30Sjl DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_handler, 234*25cf1a30Sjl ih_p->ih_handler_arg1, ih_p->ih_handler_arg2); 235*25cf1a30Sjl 236*25cf1a30Sjl if (ret != DDI_SUCCESS) { 237*25cf1a30Sjl goto fail4; 238*25cf1a30Sjl } 239*25cf1a30Sjl /* Save the pil for this ino */ 240*25cf1a30Sjl ino_p->pino_pil = hdlp->ih_pri; 241*25cf1a30Sjl 242*25cf1a30Sjl /* clear and enable interrupt */ 243*25cf1a30Sjl PCMU_IB_INO_INTR_CLEAR(ino_p->pino_clr_reg); 244*25cf1a30Sjl 245*25cf1a30Sjl /* select cpu for sharing and removal */ 246*25cf1a30Sjl cpu_id = pcmu_intr_dist_cpuid(pib_p, ino_p); 247*25cf1a30Sjl ino_p->pino_cpuid = cpu_id; 248*25cf1a30Sjl ino_p->pino_established = 1; 249*25cf1a30Sjl intr_dist_cpuid_add_device_weight(cpu_id, rdip, 0); 250*25cf1a30Sjl 251*25cf1a30Sjl cpu_id = u2u_translate_tgtid(pib_p->pib_pcmu_p, 252*25cf1a30Sjl cpu_id, ino_p->pino_map_reg); 253*25cf1a30Sjl *ino_p->pino_map_reg = ib_get_map_reg(mondo, cpu_id); 254*25cf1a30Sjl *ino_p->pino_map_reg; 255*25cf1a30Sjl ino_done: 256*25cf1a30Sjl mutex_exit(&pib_p->pib_ino_lst_mutex); 257*25cf1a30Sjl done: 258*25cf1a30Sjl PCMU_DBG2(PCMU_DBG_A_INTX, dip, "done! Interrupt 0x%x pil=%x\n", 259*25cf1a30Sjl hdlp->ih_vector, hdlp->ih_pri); 260*25cf1a30Sjl return (DDI_SUCCESS); 261*25cf1a30Sjl fail4: 262*25cf1a30Sjl pcmu_ib_delete_ino(pib_p, ino_p); 263*25cf1a30Sjl fail3: 264*25cf1a30Sjl if (ih_p->ih_config_handle) 265*25cf1a30Sjl pci_config_teardown(&ih_p->ih_config_handle); 266*25cf1a30Sjl mutex_exit(&pib_p->pib_ino_lst_mutex); 267*25cf1a30Sjl kmem_free(ih_p, sizeof (ih_t)); 268*25cf1a30Sjl fail1: 269*25cf1a30Sjl PCMU_DBG2(PCMU_DBG_A_INTX, dip, "Failed! Interrupt 0x%x pil=%x\n", 270*25cf1a30Sjl hdlp->ih_vector, hdlp->ih_pri); 271*25cf1a30Sjl return (DDI_FAILURE); 272*25cf1a30Sjl } 273*25cf1a30Sjl 274*25cf1a30Sjl int 275*25cf1a30Sjl pcmu_remove_intr(dev_info_t *dip, dev_info_t *rdip, 276*25cf1a30Sjl ddi_intr_handle_impl_t *hdlp) 277*25cf1a30Sjl { 278*25cf1a30Sjl pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip)); 279*25cf1a30Sjl pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p; 280*25cf1a30Sjl pcmu_ib_ino_t ino; 281*25cf1a30Sjl pcmu_ib_mondo_t mondo; 282*25cf1a30Sjl pcmu_ib_ino_info_t *ino_p; /* non-pulse only */ 283*25cf1a30Sjl ih_t *ih_p; /* non-pulse only */ 284*25cf1a30Sjl 285*25cf1a30Sjl ino = PCMU_IB_MONDO_TO_INO(hdlp->ih_vector); 286*25cf1a30Sjl 287*25cf1a30Sjl PCMU_DBG3(PCMU_DBG_R_INTX, dip, "pcmu_rem_intr: rdip=%s%d ino=%x\n", 288*25cf1a30Sjl ddi_driver_name(rdip), ddi_get_instance(rdip), ino); 289*25cf1a30Sjl 290*25cf1a30Sjl /* Translate the interrupt property */ 291*25cf1a30Sjl mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p, ino); 292*25cf1a30Sjl if (mondo == 0) { 293*25cf1a30Sjl PCMU_DBG1(PCMU_DBG_R_INTX, dip, 294*25cf1a30Sjl "can't get mondo for ino %x\n", ino); 295*25cf1a30Sjl return (DDI_FAILURE); 296*25cf1a30Sjl } 297*25cf1a30Sjl ino = PCMU_IB_MONDO_TO_INO(mondo); 298*25cf1a30Sjl 299*25cf1a30Sjl mutex_enter(&pib_p->pib_ino_lst_mutex); 300*25cf1a30Sjl ino_p = pcmu_ib_locate_ino(pib_p, ino); 301*25cf1a30Sjl if (!ino_p) { 302*25cf1a30Sjl mutex_exit(&pib_p->pib_ino_lst_mutex); 303*25cf1a30Sjl return (DDI_SUCCESS); 304*25cf1a30Sjl } 305*25cf1a30Sjl 306*25cf1a30Sjl ih_p = pcmu_ib_ino_locate_intr(ino_p, rdip, hdlp->ih_inum); 307*25cf1a30Sjl pcmu_ib_ino_rem_intr(pcmu_p, ino_p, ih_p); 308*25cf1a30Sjl intr_dist_cpuid_rem_device_weight(ino_p->pino_cpuid, rdip); 309*25cf1a30Sjl if (ino_p->pino_ih_size == 0) { 310*25cf1a30Sjl PCMU_IB_INO_INTR_PEND(ib_clear_intr_reg_addr(pib_p, ino)); 311*25cf1a30Sjl hdlp->ih_vector = mondo; 312*25cf1a30Sjl i_ddi_rem_ivintr(hdlp); 313*25cf1a30Sjl pcmu_ib_delete_ino(pib_p, ino_p); 314*25cf1a30Sjl } 315*25cf1a30Sjl 316*25cf1a30Sjl /* re-enable interrupt only if mapping register still shared */ 317*25cf1a30Sjl if (ino_p->pino_ih_size) { 318*25cf1a30Sjl PCMU_IB_INO_INTR_ON(ino_p->pino_map_reg); 319*25cf1a30Sjl *ino_p->pino_map_reg; 320*25cf1a30Sjl } 321*25cf1a30Sjl mutex_exit(&pib_p->pib_ino_lst_mutex); 322*25cf1a30Sjl if (ino_p->pino_ih_size == 0) { 323*25cf1a30Sjl kmem_free(ino_p, sizeof (pcmu_ib_ino_info_t)); 324*25cf1a30Sjl } 325*25cf1a30Sjl PCMU_DBG1(PCMU_DBG_R_INTX, dip, "success! mondo=%x\n", mondo); 326*25cf1a30Sjl return (DDI_SUCCESS); 327*25cf1a30Sjl } 328*25cf1a30Sjl 329*25cf1a30Sjl /* 330*25cf1a30Sjl * free the pcmu_inos array allocated during pcmu_intr_setup. the actual 331*25cf1a30Sjl * interrupts are torn down by their respective block destroy routines: 332*25cf1a30Sjl * cb_destroy, pcmu_pbm_destroy, and ib_destroy. 333*25cf1a30Sjl */ 334*25cf1a30Sjl void 335*25cf1a30Sjl pcmu_intr_teardown(pcmu_t *pcmu_p) 336*25cf1a30Sjl { 337*25cf1a30Sjl kmem_free(pcmu_p->pcmu_inos, pcmu_p->pcmu_inos_len); 338*25cf1a30Sjl pcmu_p->pcmu_inos = NULL; 339*25cf1a30Sjl pcmu_p->pcmu_inos_len = 0; 340*25cf1a30Sjl } 341