/* $Id$ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Alan Mayer */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern void hubni_eint_init(cnodeid_t cnode); extern void hubii_eint_init(cnodeid_t cnode); extern void hubii_eint_handler (int irq, void *arg, struct pt_regs *ep); extern void snia_error_intr_handler(int irq, void *devid, struct pt_regs *pt_regs); extern int maxcpus; #define HUB_ERROR_PERIOD (120 * HZ) /* 2 minutes */ void hub_error_clear(nasid_t nasid) { int i; hubreg_t idsr; int sn; for(sn=0; snel_spool_cur_addr[0] = SN0_ERROR_LOG(cnode)->el_spool_last_addr[0] = REMOTE_HUB_PI_L(nasid, sn, PI_ERR_STACK_ADDR_A); } if (REMOTE_HUB_PI_L(nasid, sn, PI_CPU_PRESENT_B)) { SN0_ERROR_LOG(cnode)->el_spool_cur_addr[1] = SN0_ERROR_LOG(cnode)->el_spool_last_addr[1] = REMOTE_HUB_PI_L(nasid, sn, PI_ERR_STACK_ADDR_B); } } PI_SPOOL_SIZE_BYTES = ERR_STACK_SIZE_BYTES(REMOTE_HUB_L(nasid, PI_ERR_STACK_SIZE)); #ifdef BRINGUP /* BRINGUP: The following code looks like a check to make sure the prom set up the error spool correctly for 2 processors. I don't think it is needed. */ for(sn=0; snel_spool_cur_addr[1] = SN0_ERROR_LOG(cnode)->el_spool_last_addr[1] = REMOTE_HUB_PI_L(nasid, sn, PI_ERR_STACK_ADDR_B); } } } #endif /* BRINGUP */ /* programming our own hub. Enable error_int_pend intr. * If both present, CPU A takes CPU b's error interrupts and any * generic ones. CPU B takes CPU A error ints. */ if (cause_intr_connect (SRB_ERR_IDX, (intr_func_t)(hubpi_eint_handler), SR_ALL_MASK|SR_IE)) { cmn_err(ERR_WARN, "hub_error_init: cause_intr_connect failed on %d", cnode); } } else { /* programming remote hub. The only valid reason that this * is called will be on headless hubs. No interrupts */ for(sn=0; snhuberror_ticks = HUB_ERROR_PERIOD; return; } /* * Function : hubii_eint_init * Parameters : cnode * Purpose : to initialize the hub iio error interrupt. * Assumptions : Called once per hub, by the cpu which will ultimately * handle this interrupt. * Returns : None. */ void hubii_eint_init(cnodeid_t cnode) { int bit, rv; ii_iidsr_u_t hubio_eint; hubinfo_t hinfo; cpuid_t intr_cpu; devfs_handle_t hub_v; ii_ilcsr_u_t ilcsr; hub_v = (devfs_handle_t)cnodeid_to_vertex(cnode); ASSERT_ALWAYS(hub_v); hubinfo_get(hub_v, &hinfo); ASSERT(hinfo); ASSERT(hinfo->h_cnodeid == cnode); ilcsr.ii_ilcsr_regval = REMOTE_HUB_L(hinfo->h_nasid, IIO_ILCSR); if ((ilcsr.ii_ilcsr_fld_s.i_llp_stat & 0x2) == 0) { /* * HUB II link is not up. * Just disable LLP, and don't connect any interrupts. */ ilcsr.ii_ilcsr_fld_s.i_llp_en = 0; REMOTE_HUB_S(hinfo->h_nasid, IIO_ILCSR, ilcsr.ii_ilcsr_regval); return; } /* Select a possible interrupt target where there is a free interrupt * bit and also reserve the interrupt bit for this IO error interrupt */ intr_cpu = intr_heuristic(hub_v,0,INTRCONNECT_ANYBIT,II_ERRORINT,hub_v, "HUB IO error interrupt",&bit); if (intr_cpu == CPU_NONE) { printk("hubii_eint_init: intr_reserve_level failed, cnode %d", cnode); return; } rv = intr_connect_level(intr_cpu, bit, 0,(intr_func_t)(NULL), (void *)(long)hub_v, NULL); synergy_intr_connect(bit, intr_cpu); request_irq(bit_pos_to_irq(bit) + (intr_cpu << 8), hubii_eint_handler, 0, NULL, (void *)hub_v); ASSERT_ALWAYS(rv >= 0); hubio_eint.ii_iidsr_regval = 0; hubio_eint.ii_iidsr_fld_s.i_enable = 1; hubio_eint.ii_iidsr_fld_s.i_level = bit;/* Take the least significant bits*/ hubio_eint.ii_iidsr_fld_s.i_node = COMPACT_TO_NASID_NODEID(cnode); hubio_eint.ii_iidsr_fld_s.i_pi_id = cpuid_to_subnode(intr_cpu); REMOTE_HUB_S(hinfo->h_nasid, IIO_IIDSR, hubio_eint.ii_iidsr_regval); } void hubni_eint_init(cnodeid_t cnode) { int intr_bit; cpuid_t targ; if ((targ = cnodeid_to_cpuid(cnode)) == CPU_NONE) return; /* The prom chooses which cpu gets these interrupts, but we * don't know which one it chose. We will register all of the * cpus to be sure. This only costs us an irqaction per cpu. */ for (; targ < CPUS_PER_NODE; targ++) { if (!cpu_enabled(targ) ) continue; /* connect the INTEND1 bits. */ for (intr_bit = XB_ERROR; intr_bit <= MSC_PANIC_INTR; intr_bit++) { intr_connect_level(targ, intr_bit, II_ERRORINT, NULL, NULL, NULL); } request_irq(SGI_HUB_ERROR_IRQ + (targ << 8), snia_error_intr_handler, 0, NULL, NULL); /* synergy masks are initialized in the prom to enable all interrupts. */ /* We'll just leave them that way, here, for these interrupts. */ } } /*ARGSUSED*/ void hubii_eint_handler (int irq, void *arg, struct pt_regs *ep) { devfs_handle_t hub_v; hubinfo_t hinfo; ii_wstat_u_t wstat; hubreg_t idsr; panic("Hubii interrupt\n"); #ifdef ajm /* * If the NI has a problem, everyone has a problem. We shouldn't * even attempt to handle other errors when an NI error is present. */ if (check_ni_errors()) { hubni_error_handler("II interrupt", 1); /* NOTREACHED */ } /* two levels of casting avoids compiler warning.!! */ hub_v = (devfs_handle_t)(long)(arg); ASSERT(hub_v); hubinfo_get(hub_v, &hinfo); /* * Identify the reason for error. */ wstat.ii_wstat_regval = REMOTE_HUB_L(hinfo->h_nasid, IIO_WSTAT); if (wstat.ii_wstat_fld_s.w_crazy) { char *reason; /* * We can do a couple of things here. * Look at the fields TX_MX_RTY/XT_TAIL_TO/XT_CRD_TO to check * which of these caused the CRAZY bit to be set. * You may be able to check if the Link is up really. */ if (wstat.ii_wstat_fld_s.w_tx_mx_rty) reason = "Micro Packet Retry Timeout"; else if (wstat.ii_wstat_fld_s.w_xt_tail_to) reason = "Crosstalk Tail Timeout"; else if (wstat.ii_wstat_fld_s.w_xt_crd_to) reason = "Crosstalk Credit Timeout"; else { hubreg_t hubii_imem; /* * Check if widget 0 has been marked as shutdown, or * if BTE 0/1 has been marked. */ hubii_imem = REMOTE_HUB_L(hinfo->h_nasid, IIO_IMEM); if (hubii_imem & IIO_IMEM_W0ESD) reason = "Hub Widget 0 has been Shutdown"; else if (hubii_imem & IIO_IMEM_B0ESD) reason = "BTE 0 has been shutdown"; else if (hubii_imem & IIO_IMEM_B1ESD) reason = "BTE 1 has been shutdown"; else reason = "Unknown"; } /* * Note: we may never be able to print this, if the II talking * to Xbow which hosts the console is dead. */ printk("Hub %d to Xtalk Link failed (II_ECRAZY) Reason: %s", hinfo->h_cnodeid, reason); } /* * It's a toss as to which one among PRB/CRB to check first. * Current decision is based on the severity of the errors. * IO CRB errors tend to be more severe than PRB errors. * * It is possible for BTE errors to have been handled already, so we * may not see any errors handled here. */ (void)hubiio_crb_error_handler(hub_v, hinfo); (void)hubiio_prb_error_handler(hub_v, hinfo); /* * If we reach here, it indicates crb/prb handlers successfully * handled the error. So, re-enable II to send more interrupt * and return. */ REMOTE_HUB_S(hinfo->h_nasid, IIO_IECLR, 0xffffff); idsr = REMOTE_HUB_L(hinfo->h_nasid, IIO_IIDSR) & ~IIO_IIDSR_SENT_MASK; REMOTE_HUB_S(hinfo->h_nasid, IIO_IIDSR, idsr); #endif /* ajm */ }