| /* | |
| * 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) 2000-2007 Silicon Graphics, Inc. All Rights Reserved. | |
| */ | |
| #include <linux/types.h> | |
| #include <asm/sn/sn_sal.h> | |
| #include "ioerror.h" | |
| #include <asm/sn/addrs.h> | |
| #include <asm/sn/shubio.h> | |
| #include <asm/sn/geo.h> | |
| #include "xtalk/xwidgetdev.h" | |
| #include "xtalk/hubdev.h" | |
| #include <asm/sn/bte.h> | |
| #include <asm/param.h> | |
| /* | |
| * Bte error handling is done in two parts. The first captures | |
| * any crb related errors. Since there can be multiple crbs per | |
| * interface and multiple interfaces active, we need to wait until | |
| * all active crbs are completed. This is the first job of the | |
| * second part error handler. When all bte related CRBs are cleanly | |
| * completed, it resets the interfaces and gets them ready for new | |
| * transfers to be queued. | |
| */ | |
| /* | |
| * Wait until all BTE related CRBs are completed | |
| * and then reset the interfaces. | |
| */ | |
| static int shub1_bte_error_handler(struct nodepda_s *err_nodepda) | |
| { | |
| struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer; | |
| nasid_t nasid; | |
| int i; | |
| int valid_crbs; | |
| ii_imem_u_t imem; /* II IMEM Register */ | |
| ii_icrb0_d_u_t icrbd; /* II CRB Register D */ | |
| ii_ibcr_u_t ibcr; | |
| ii_icmr_u_t icmr; | |
| ii_ieclr_u_t ieclr; | |
| BTE_PRINTK(("shub1_bte_error_handler(%p) - %d\n", err_nodepda, | |
| smp_processor_id())); | |
| if ((err_nodepda->bte_if[0].bh_error == BTE_SUCCESS) && | |
| (err_nodepda->bte_if[1].bh_error == BTE_SUCCESS)) { | |
| BTE_PRINTK(("eh:%p:%d Nothing to do.\n", err_nodepda, | |
| smp_processor_id())); | |
| return 1; | |
| } | |
| /* Determine information about our hub */ | |
| nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode); | |
| /* | |
| * A BTE transfer can use multiple CRBs. We need to make sure | |
| * that all the BTE CRBs are complete (or timed out) before | |
| * attempting to clean up the error. Resetting the BTE while | |
| * there are still BTE CRBs active will hang the BTE. | |
| * We should look at all the CRBs to see if they are allocated | |
| * to the BTE and see if they are still active. When none | |
| * are active, we can continue with the cleanup. | |
| * | |
| * We also want to make sure that the local NI port is up. | |
| * When a router resets the NI port can go down, while it | |
| * goes through the LLP handshake, but then comes back up. | |
| */ | |
| icmr.ii_icmr_regval = REMOTE_HUB_L(nasid, IIO_ICMR); | |
| if (icmr.ii_icmr_fld_s.i_crb_mark != 0) { | |
| /* | |
| * There are errors which still need to be cleaned up by | |
| * hubiio_crb_error_handler | |
| */ | |
| mod_timer(recovery_timer, jiffies + (HZ * 5)); | |
| BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda, | |
| smp_processor_id())); | |
| return 1; | |
| } | |
| if (icmr.ii_icmr_fld_s.i_crb_vld != 0) { | |
| valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld; | |
| for (i = 0; i < IIO_NUM_CRBS; i++) { | |
| if (!((1 << i) & valid_crbs)) { | |
| /* This crb was not marked as valid, ignore */ | |
| continue; | |
| } | |
| icrbd.ii_icrb0_d_regval = | |
| REMOTE_HUB_L(nasid, IIO_ICRB_D(i)); | |
| if (icrbd.d_bteop) { | |
| mod_timer(recovery_timer, jiffies + (HZ * 5)); | |
| BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n", | |
| err_nodepda, smp_processor_id(), | |
| i)); | |
| return 1; | |
| } | |
| } | |
| } | |
| BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda, smp_processor_id())); | |
| /* Re-enable both bte interfaces */ | |
| imem.ii_imem_regval = REMOTE_HUB_L(nasid, IIO_IMEM); | |
| imem.ii_imem_fld_s.i_b0_esd = imem.ii_imem_fld_s.i_b1_esd = 1; | |
| REMOTE_HUB_S(nasid, IIO_IMEM, imem.ii_imem_regval); | |
| /* Clear BTE0/1 error bits */ | |
| ieclr.ii_ieclr_regval = 0; | |
| if (err_nodepda->bte_if[0].bh_error != BTE_SUCCESS) | |
| ieclr.ii_ieclr_fld_s.i_e_bte_0 = 1; | |
| if (err_nodepda->bte_if[1].bh_error != BTE_SUCCESS) | |
| ieclr.ii_ieclr_fld_s.i_e_bte_1 = 1; | |
| REMOTE_HUB_S(nasid, IIO_IECLR, ieclr.ii_ieclr_regval); | |
| /* Reinitialize both BTE state machines. */ | |
| ibcr.ii_ibcr_regval = REMOTE_HUB_L(nasid, IIO_IBCR); | |
| ibcr.ii_ibcr_fld_s.i_soft_reset = 1; | |
| REMOTE_HUB_S(nasid, IIO_IBCR, ibcr.ii_ibcr_regval); | |
| del_timer(recovery_timer); | |
| return 0; | |
| } | |
| /* | |
| * Wait until all BTE related CRBs are completed | |
| * and then reset the interfaces. | |
| */ | |
| static int shub2_bte_error_handler(struct nodepda_s *err_nodepda) | |
| { | |
| struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer; | |
| struct bteinfo_s *bte; | |
| nasid_t nasid; | |
| u64 status; | |
| int i; | |
| nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode); | |
| /* | |
| * Verify that all the BTEs are complete | |
| */ | |
| for (i = 0; i < BTES_PER_NODE; i++) { | |
| bte = &err_nodepda->bte_if[i]; | |
| status = BTE_LNSTAT_LOAD(bte); | |
| if (status & IBLS_ERROR) { | |
| bte->bh_error = BTE_SHUB2_ERROR(status); | |
| continue; | |
| } | |
| if (!(status & IBLS_BUSY)) | |
| continue; | |
| mod_timer(recovery_timer, jiffies + (HZ * 5)); | |
| BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda, | |
| smp_processor_id())); | |
| return 1; | |
| } | |
| if (ia64_sn_bte_recovery(nasid)) | |
| panic("bte_error_handler(): Fatal BTE Error"); | |
| del_timer(recovery_timer); | |
| return 0; | |
| } | |
| /* | |
| * Wait until all BTE related CRBs are completed | |
| * and then reset the interfaces. | |
| */ | |
| void bte_error_handler(struct nodepda_s *err_nodepda) | |
| { | |
| spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock; | |
| int i; | |
| unsigned long irq_flags; | |
| volatile u64 *notify; | |
| bte_result_t bh_error; | |
| BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda, | |
| smp_processor_id())); | |
| spin_lock_irqsave(recovery_lock, irq_flags); | |
| /* | |
| * Lock all interfaces on this node to prevent new transfers | |
| * from being queued. | |
| */ | |
| for (i = 0; i < BTES_PER_NODE; i++) { | |
| if (err_nodepda->bte_if[i].cleanup_active) { | |
| continue; | |
| } | |
| spin_lock(&err_nodepda->bte_if[i].spinlock); | |
| BTE_PRINTK(("eh:%p:%d locked %d\n", err_nodepda, | |
| smp_processor_id(), i)); | |
| err_nodepda->bte_if[i].cleanup_active = 1; | |
| } | |
| if (is_shub1()) { | |
| if (shub1_bte_error_handler(err_nodepda)) { | |
| spin_unlock_irqrestore(recovery_lock, irq_flags); | |
| return; | |
| } | |
| } else { | |
| if (shub2_bte_error_handler(err_nodepda)) { | |
| spin_unlock_irqrestore(recovery_lock, irq_flags); | |
| return; | |
| } | |
| } | |
| for (i = 0; i < BTES_PER_NODE; i++) { | |
| bh_error = err_nodepda->bte_if[i].bh_error; | |
| if (bh_error != BTE_SUCCESS) { | |
| /* There is an error which needs to be notified */ | |
| notify = err_nodepda->bte_if[i].most_rcnt_na; | |
| BTE_PRINTK(("cnode %d bte %d error=0x%lx\n", | |
| err_nodepda->bte_if[i].bte_cnode, | |
| err_nodepda->bte_if[i].bte_num, | |
| IBLS_ERROR | (u64) bh_error)); | |
| *notify = IBLS_ERROR | bh_error; | |
| err_nodepda->bte_if[i].bh_error = BTE_SUCCESS; | |
| } | |
| err_nodepda->bte_if[i].cleanup_active = 0; | |
| BTE_PRINTK(("eh:%p:%d Unlocked %d\n", err_nodepda, | |
| smp_processor_id(), i)); | |
| spin_unlock(&err_nodepda->bte_if[i].spinlock); | |
| } | |
| spin_unlock_irqrestore(recovery_lock, irq_flags); | |
| } | |
| /* | |
| * First part error handler. This is called whenever any error CRB interrupt | |
| * is generated by the II. | |
| */ | |
| void | |
| bte_crb_error_handler(cnodeid_t cnode, int btenum, | |
| int crbnum, ioerror_t * ioe, int bteop) | |
| { | |
| struct bteinfo_s *bte; | |
| bte = &(NODEPDA(cnode)->bte_if[btenum]); | |
| /* | |
| * The caller has already figured out the error type, we save that | |
| * in the bte handle structure for the thread exercising the | |
| * interface to consume. | |
| */ | |
| bte->bh_error = ioe->ie_errortype + BTEFAIL_OFFSET; | |
| bte->bte_error_count++; | |
| BTE_PRINTK(("Got an error on cnode %d bte %d: HW error type 0x%x\n", | |
| bte->bte_cnode, bte->bte_num, ioe->ie_errortype)); | |
| bte_error_handler(NODEPDA(cnode)); | |
| } | |