| /* | |
| * 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-2007 Silicon Graphics, Inc. All rights reserved. | |
| */ | |
| #include <linux/types.h> | |
| #include <linux/interrupt.h> | |
| #include <asm/delay.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> | |
| 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 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; | |
| if (is_shub1()) { | |
| SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT, | |
| (u64) nasid, 0, 0, 0, 0, 0, 0); | |
| if ((int)ret_stuff.v0) | |
| panic("%s: Fatal %s Error", __func__, | |
| ((nasid & 1) ? "TIO" : "HUBII")); | |
| if (!(nasid & 1)) /* Not a TIO, handle CRB errors */ | |
| (void)hubiio_crb_error_handler(hubdev_info); | |
| } else | |
| if (nasid & 1) { /* TIO errors */ | |
| SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT, | |
| (u64) nasid, 0, 0, 0, 0, 0, 0); | |
| if ((int)ret_stuff.v0) | |
| panic("%s: Fatal TIO Error", __func__); | |
| } else | |
| bte_error_handler((unsigned long)NODEPDA(nasid_to_cnodeid(nasid))); | |
| 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) | |
| cpu_relax(); | |
| } | |
| /* | |
| * 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, hub_eint_handler, IRQF_SHARED, | |
| "SN_hub_error", hubdev_info)) { | |
| printk(KERN_ERR "hub_error_init: Failed to request_irq for 0x%p\n", | |
| hubdev_info); | |
| return; | |
| } | |
| irq_set_handler(SGI_II_ERROR, handle_level_irq); | |
| sn_set_err_irq_affinity(SGI_II_ERROR); | |
| } | |
| /* | |
| * 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, IRQF_SHARED, "SN_TIO_error", | |
| (void *)hubdev_info)) { | |
| printk("ice_error_init: request_irq() error hubdev_info 0x%p\n", | |
| hubdev_info); | |
| return; | |
| } | |
| irq_set_handler(SGI_TIO_ERROR, handle_level_irq); | |
| sn_set_err_irq_affinity(SGI_TIO_ERROR); | |
| } | |