aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/sn/io/sn2/bte_error.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/sn/io/sn2/bte_error.c')
-rw-r--r--arch/ia64/sn/io/sn2/bte_error.c320
1 files changed, 216 insertions, 104 deletions
diff --git a/arch/ia64/sn/io/sn2/bte_error.c b/arch/ia64/sn/io/sn2/bte_error.c
index 8e086e1c1b4f09..4ab2cb0e9938ca 100644
--- a/arch/ia64/sn/io/sn2/bte_error.c
+++ b/arch/ia64/sn/io/sn2/bte_error.c
@@ -1,10 +1,35 @@
-/* $Id: bte_error.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $
+/*
*
- * 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,2002 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
*/
@@ -29,121 +54,208 @@
#include <asm/sn/sn2/shubio.h>
#include <asm/sn/bte.h>
-/************************************************************************
- * *
- * BTE ERROR RECOVERY *
- * *
- * Given a BTE error, the node causing the error must do the following: *
- * a) Clear all crbs relating to that BTE *
- * 1) Read CRBA value for crb in question *
- * 2) Mark CRB as VALID, store local physical *
- * address known to be good in the address field *
- * (bte_notification_targ is a known good local *
- * address). *
- * 3) Write CRBA *
- * 4) Using ICCR, FLUSH the CRB, and wait for it to *
- * complete. *
- * ... BTE BUSY bit should now be clear (or at least *
- * should be after ALL CRBs associated with the *
- * transfer are complete. *
- * *
- * b) Re-enable BTE *
- * 1) Write IMEM with BTE Enable + XXX bits
- * 2) Write IECLR with BTE clear bits
- * 3) Clear IIDSR INT_SENT bits.
- * *
- ************************************************************************/
-
-/*
- * >>> bte_crb_error_handler needs to be broken into two parts. The
- * first should cleanup the CRB. The second should wait until all bte
- * related CRB's are complete and then do the error reset.
+
+/*
+ * 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.
*/
-void
-bte_crb_error_handler(devfs_handle_t hub_v, int btenum,
- int crbnum, ioerror_t *ioe, int bteop)
+
+
+void bte_error_handler(unsigned long);
+
+
/*
- * Function: bte_crb_error_handler
- * Purpose: Process a CRB for a specific HUB/BTE
- * Parameters: hub_v - vertex of hub in HW graph
- * btenum - bte number on hub (0 == a, 1 == b)
- * crbnum - crb number being processed
- * Notes:
- * This routine assumes serialization at a higher level. A CRB
- * should not be processed more than once. The error recovery
- * follows the following sequence - if you change this, be real
- * sure about what you are doing.
- *
+ * First part error handler. This is called whenever any error CRB interrupt
+ * is generated by the II.
*/
+void
+bte_crb_error_handler(vertex_hdl_t hub_v, int btenum,
+ int crbnum, ioerror_t * ioe, int bteop)
{
- hubinfo_t hinfo;
- icrba_t crba;
- icrbb_t crbb;
- nasid_t n;
- hubreg_t iidsr, imem, ieclr;
+ hubinfo_t hinfo;
+ struct bteinfo_s *bte;
+
hubinfo_get(hub_v, &hinfo);
+ bte = &hinfo->h_nodepda->bte_if[btenum];
+
+ /*
+ * The caller has already figured out the error type, we save that
+ * in the bte handle structure for the thread excercising the
+ * interface to consume.
+ */
+ switch (ioe->ie_errortype) {
+ case IIO_ICRB_ECODE_PERR:
+ bte->bh_error = BTEFAIL_POISON;
+ break;
+ case IIO_ICRB_ECODE_WERR:
+ bte->bh_error = BTEFAIL_PROT;
+ break;
+ case IIO_ICRB_ECODE_AERR:
+ bte->bh_error = BTEFAIL_ACCESS;
+ break;
+ case IIO_ICRB_ECODE_TOUT:
+ bte->bh_error = BTEFAIL_TOUT;
+ break;
+ case IIO_ICRB_ECODE_XTERR:
+ bte->bh_error = BTEFAIL_XTERR;
+ break;
+ case IIO_ICRB_ECODE_DERR:
+ bte->bh_error = BTEFAIL_DIR;
+ break;
+ case IIO_ICRB_ECODE_PWERR:
+ case IIO_ICRB_ECODE_PRERR:
+ /* NO BREAK */
+ default:
+ bte->bh_error = BTEFAIL_ERROR;
+ }
+ bte->bte_error_count++;
+
+ BTE_PRINTK(("Got an error on cnode %d bte %d\n",
+ bte->bte_cnode, bte->bte_num));
+ bte_error_handler((unsigned long) hinfo->h_nodepda);
+}
- n = hinfo->h_nasid;
-
+/*
+ * Second part error handler. Wait until all BTE related CRBs are completed
+ * and then reset the interfaces.
+ */
+void
+bte_error_handler(unsigned long _nodepda)
+{
+ struct nodepda_s *err_nodepda = (struct nodepda_s *) _nodepda;
+ spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock;
+ struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer;
+ nasid_t nasid;
+ int i;
+ int valid_crbs;
+ unsigned long irq_flags;
+ volatile u64 *notify;
+ bte_result_t bh_error;
+ 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;
+
+
+ BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda,
+ smp_processor_id()));
+
+ spin_lock_irqsave(recovery_lock, irq_flags);
+
+ 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()));
+ spin_unlock_irqrestore(recovery_lock, irq_flags);
+ return;
+ }
/*
- * The following 10 lines (or so) are adapted from IRIXs
- * bte_crb_error function. No clear documentation tells
- * why the crb needs to complete normally in order for
- * the BTE to resume normal operations. This first step
- * appears vital!
+ * 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;
+ }
+
+ /* Determine information about our hub */
+ nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode);
+
/*
- * Zero error and error code to prevent error_dump complaining
- * about these CRBs. Copy the CRB to the notification line.
- * The crb address is in shub format (physical address shifted
- * right by cacheline size).
+ * 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.
*/
- crbb.ii_icrb0_b_regval = REMOTE_HUB_L(n, IIO_ICRB_B(crbnum));
- crbb.b_error=0;
- crbb.b_ecode=0;
- REMOTE_HUB_S(n, IIO_ICRB_B(crbnum), crbb.ii_icrb0_b_regval);
-
- crba.ii_icrb0_a_regval = REMOTE_HUB_L(n, IIO_ICRB_A(crbnum));
- crba.a_addr = TO_PHYS((u64)&nodepda->bte_if[btenum].notify) >> 3;
- crba.a_valid = 1;
- REMOTE_HUB_S(n, IIO_ICRB_A(crbnum), crba.ii_icrb0_a_regval);
-
- REMOTE_HUB_S(n, IIO_ICCR,
- IIO_ICCR_PENDING | IIO_ICCR_CMD_FLUSH | crbnum);
-
- while (REMOTE_HUB_L(n, IIO_ICCR) & IIO_ICCR_PENDING)
- ;
-
-
- /* Terminate the BTE. */
- /* >>> The other bte transfer will need to be restarted. */
- HUB_L((shubreg_t *)((nodepda->bte_if[btenum].bte_base_addr +
- IIO_IBCT0 - IIO_IBLS0)));
-
- imem = REMOTE_HUB_L(n, IIO_IMEM);
- ieclr = REMOTE_HUB_L(n, IIO_IECLR);
- if (btenum == 0) {
- imem |= IIO_IMEM_W0ESD | IIO_IMEM_B0ESD;
- ieclr|= IECLR_BTE0;
- } else {
- imem |= IIO_IMEM_W0ESD | IIO_IMEM_B1ESD;
- ieclr|= IECLR_BTE1;
+ 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, HZ * 5);
+ BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda,
+ smp_processor_id()));
+ spin_unlock_irqrestore(recovery_lock, irq_flags);
+ return;
}
- REMOTE_HUB_S(n, IIO_IMEM, imem);
- REMOTE_HUB_S(n, IIO_IECLR, ieclr);
-
- iidsr = REMOTE_HUB_L(n, IIO_IIDSR);
- iidsr &= ~IIO_IIDSR_SENT_MASK;
- iidsr |= IIO_IIDSR_ENB_MASK;
- REMOTE_HUB_S(n, IIO_IIDSR, iidsr);
+ if (icmr.ii_icmr_fld_s.i_crb_vld != 0) {
+ valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld;
- bte_reset_nasid(n);
+ 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, HZ * 5);
+ BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n",
+ err_nodepda, smp_processor_id(), i));
+ spin_unlock_irqrestore(recovery_lock,
+ irq_flags);
+ return;
+ }
+ }
+ }
- *nodepda->bte_if[btenum].most_rcnt_na = IBLS_ERROR;
-}
+ BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda,
+ smp_processor_id()));
+ /* Reenable 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);
+
+ /* 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);
+
+
+ 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(&pda->cpu_bte_if[i]->spinlock);
+ }
+
+ del_timer(recovery_timer);
+
+ spin_unlock_irqrestore(recovery_lock, irq_flags);
+}