This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / arch / ia64 / sn / kernel / huberror.c
diff --git a/arch/ia64/sn/kernel/huberror.c b/arch/ia64/sn/kernel/huberror.c
new file mode 100644 (file)
index 0000000..1180604
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/delay.h>
+#include <asm/sn/sn_sal.h>
+#include "ioerror.h"
+#include <asm/sn/addrs.h>
+#include "shubio.h"
+#include <asm/sn/geo.h>
+#include "xtalk/xwidgetdev.h"
+#include "xtalk/hubdev.h"
+#include <asm/sn/bte.h>
+
+void hubiio_crb_error_handler(struct hubdev_info *hubdev_info);
+extern void bte_crb_error_handler(cnodeid_t, int, int, ioerror_t *,
+                                 int);
+static irqreturn_t hub_eint_handler(int irq, void *arg, struct pt_regs *ep)
+{
+       struct hubdev_info *hubdev_info;
+       struct ia64_sal_retval ret_stuff;
+       nasid_t nasid;
+
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+       hubdev_info = (struct hubdev_info *)arg;
+       nasid = hubdev_info->hdi_nasid;
+       SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT,
+                       (u64) nasid, 0, 0, 0, 0, 0, 0);
+
+       if ((int)ret_stuff.v0)
+               panic("hubii_eint_handler(): Fatal TIO Error");
+
+       if (!(nasid & 1)) /* Not a TIO, handle CRB errors */
+               (void)hubiio_crb_error_handler(hubdev_info);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Free the hub CRB "crbnum" which encountered an error.
+ * Assumption is, error handling was successfully done,
+ * and we now want to return the CRB back to Hub for normal usage.
+ *
+ * In order to free the CRB, all that's needed is to de-allocate it
+ *
+ * Assumption:
+ *      No other processor is mucking around with the hub control register.
+ *      So, upper layer has to single thread this.
+ */
+void hubiio_crb_free(struct hubdev_info *hubdev_info, int crbnum)
+{
+       ii_icrb0_b_u_t icrbb;
+
+       /*
+        * The hardware does NOT clear the mark bit, so it must get cleared
+        * here to be sure the error is not processed twice.
+        */
+       icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(hubdev_info->hdi_nasid,
+                                              IIO_ICRB_B(crbnum));
+       icrbb.b_mark = 0;
+       REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICRB_B(crbnum),
+                    icrbb.ii_icrb0_b_regval);
+       /*
+        * Deallocate the register wait till hub indicates it's done.
+        */
+       REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICDR, (IIO_ICDR_PND | crbnum));
+       while (REMOTE_HUB_L(hubdev_info->hdi_nasid, IIO_ICDR) & IIO_ICDR_PND)
+               udelay(1);
+
+}
+
+/*
+ * hubiio_crb_error_handler
+ *
+ *     This routine gets invoked when a hub gets an error 
+ *     interrupt. So, the routine is running in interrupt context
+ *     at error interrupt level.
+ * Action:
+ *     It's responsible for identifying ALL the CRBs that are marked
+ *     with error, and process them. 
+ *     
+ *     If you find the CRB that's marked with error, map this to the
+ *     reason it caused error, and invoke appropriate error handler.
+ *
+ *     XXX Be aware of the information in the context register.
+ *
+ * NOTE:
+ *     Use REMOTE_HUB_* macro instead of LOCAL_HUB_* so that the interrupt
+ *     handler can be run on any node. (not necessarily the node 
+ *     corresponding to the hub that encountered error).
+ */
+
+void hubiio_crb_error_handler(struct hubdev_info *hubdev_info)
+{
+       nasid_t nasid;
+       ii_icrb0_a_u_t icrba;   /* II CRB Register A */
+       ii_icrb0_b_u_t icrbb;   /* II CRB Register B */
+       ii_icrb0_c_u_t icrbc;   /* II CRB Register C */
+       ii_icrb0_d_u_t icrbd;   /* II CRB Register D */
+       ii_icrb0_e_u_t icrbe;   /* II CRB Register D */
+       int i;
+       int num_errors = 0;     /* Num of errors handled */
+       ioerror_t ioerror;
+
+       nasid = hubdev_info->hdi_nasid;
+
+       /*
+        * XXX - Add locking for any recovery actions
+        */
+       /*
+        * Scan through all CRBs in the Hub, and handle the errors
+        * in any of the CRBs marked.
+        */
+       for (i = 0; i < IIO_NUM_CRBS; i++) {
+               /* Check this crb entry to see if it is in error. */
+               icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(nasid, IIO_ICRB_B(i));
+
+               if (icrbb.b_mark == 0) {
+                       continue;
+               }
+
+               icrba.ii_icrb0_a_regval = REMOTE_HUB_L(nasid, IIO_ICRB_A(i));
+
+               IOERROR_INIT(&ioerror);
+
+               /* read other CRB error registers. */
+               icrbc.ii_icrb0_c_regval = REMOTE_HUB_L(nasid, IIO_ICRB_C(i));
+               icrbd.ii_icrb0_d_regval = REMOTE_HUB_L(nasid, IIO_ICRB_D(i));
+               icrbe.ii_icrb0_e_regval = REMOTE_HUB_L(nasid, IIO_ICRB_E(i));
+
+               IOERROR_SETVALUE(&ioerror, errortype, icrbb.b_ecode);
+
+               /* Check if this error is due to BTE operation,
+                * and handle it separately.
+                */
+               if (icrbd.d_bteop ||
+                   ((icrbb.b_initiator == IIO_ICRB_INIT_BTE0 ||
+                     icrbb.b_initiator == IIO_ICRB_INIT_BTE1) &&
+                    (icrbb.b_imsgtype == IIO_ICRB_IMSGT_BTE ||
+                     icrbb.b_imsgtype == IIO_ICRB_IMSGT_SN1NET))) {
+
+                       int bte_num;
+
+                       if (icrbd.d_bteop)
+                               bte_num = icrbc.c_btenum;
+                       else    /* b_initiator bit 2 gives BTE number */
+                               bte_num = (icrbb.b_initiator & 0x4) >> 2;
+
+                       hubiio_crb_free(hubdev_info, i);
+
+                       bte_crb_error_handler(nasid_to_cnodeid(nasid), bte_num,
+                                             i, &ioerror, icrbd.d_bteop);
+                       num_errors++;
+                       continue;
+               }
+       }
+}
+
+/*
+ * Function    : hub_error_init
+ * Purpose     : initialize the error handling requirements for a given hub.
+ * Parameters  : cnode, the compact nodeid.
+ * Assumptions : Called only once per hub, either by a local cpu. Or by a
+ *                     remote cpu, when this hub is headless.(cpuless)
+ * Returns     : None
+ */
+void hub_error_init(struct hubdev_info *hubdev_info)
+{
+       if (request_irq(SGI_II_ERROR, (void *)hub_eint_handler, SA_SHIRQ,
+                       "SN_hub_error", (void *)hubdev_info))
+               printk("hub_error_init: Failed to request_irq for 0x%p\n",
+                   hubdev_info);
+       return;
+}
+
+
+/*
+ * Function    : ice_error_init
+ * Purpose     : initialize the error handling requirements for a given tio.
+ * Parameters  : cnode, the compact nodeid.
+ * Assumptions : Called only once per tio.
+ * Returns     : None
+ */
+void ice_error_init(struct hubdev_info *hubdev_info)
+{
+        if (request_irq
+            (SGI_TIO_ERROR, (void *)hub_eint_handler, SA_SHIRQ, "SN_TIO_error",
+             (void *)hubdev_info))
+                printk("ice_error_init: request_irq() error hubdev_info 0x%p\n",
+                       hubdev_info);
+        return;
+}
+