1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 1992 - 1997, 2000,2002-2007 Silicon Graphics, Inc. All rights reserved. 7 */ 8 9#include <linux/types.h> 10#include <linux/interrupt.h> 11#include <asm/delay.h> 12#include <asm/sn/sn_sal.h> 13#include "ioerror.h" 14#include <asm/sn/addrs.h> 15#include <asm/sn/shubio.h> 16#include <asm/sn/geo.h> 17#include "xtalk/xwidgetdev.h" 18#include "xtalk/hubdev.h" 19#include <asm/sn/bte.h> 20 21void hubiio_crb_error_handler(struct hubdev_info *hubdev_info); 22extern void bte_crb_error_handler(cnodeid_t, int, int, ioerror_t *, 23 int); 24static irqreturn_t hub_eint_handler(int irq, void *arg) 25{ 26 struct hubdev_info *hubdev_info; 27 struct ia64_sal_retval ret_stuff; 28 nasid_t nasid; 29 30 ret_stuff.status = 0; 31 ret_stuff.v0 = 0; 32 hubdev_info = (struct hubdev_info *)arg; 33 nasid = hubdev_info->hdi_nasid; 34 35 if (is_shub1()) { 36 SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT, 37 (u64) nasid, 0, 0, 0, 0, 0, 0); 38 39 if ((int)ret_stuff.v0) 40 panic("%s: Fatal %s Error", __func__, 41 ((nasid & 1) ? "TIO" : "HUBII")); 42 43 if (!(nasid & 1)) /* Not a TIO, handle CRB errors */ 44 (void)hubiio_crb_error_handler(hubdev_info); 45 } else 46 if (nasid & 1) { /* TIO errors */ 47 SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT, 48 (u64) nasid, 0, 0, 0, 0, 0, 0); 49 50 if ((int)ret_stuff.v0) 51 panic("%s: Fatal TIO Error", __func__); 52 } else 53 bte_error_handler((unsigned long)NODEPDA(nasid_to_cnodeid(nasid))); 54 55 return IRQ_HANDLED; 56} 57 58/* 59 * Free the hub CRB "crbnum" which encountered an error. 60 * Assumption is, error handling was successfully done, 61 * and we now want to return the CRB back to Hub for normal usage. 62 * 63 * In order to free the CRB, all that's needed is to de-allocate it 64 * 65 * Assumption: 66 * No other processor is mucking around with the hub control register. 67 * So, upper layer has to single thread this. 68 */ 69void hubiio_crb_free(struct hubdev_info *hubdev_info, int crbnum) 70{ 71 ii_icrb0_b_u_t icrbb; 72 73 /* 74 * The hardware does NOT clear the mark bit, so it must get cleared 75 * here to be sure the error is not processed twice. 76 */ 77 icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(hubdev_info->hdi_nasid, 78 IIO_ICRB_B(crbnum)); 79 icrbb.b_mark = 0; 80 REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICRB_B(crbnum), 81 icrbb.ii_icrb0_b_regval); 82 /* 83 * Deallocate the register wait till hub indicates it's done. 84 */ 85 REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICDR, (IIO_ICDR_PND | crbnum)); 86 while (REMOTE_HUB_L(hubdev_info->hdi_nasid, IIO_ICDR) & IIO_ICDR_PND) 87 cpu_relax(); 88 89} 90 91/* 92 * hubiio_crb_error_handler 93 * 94 * This routine gets invoked when a hub gets an error 95 * interrupt. So, the routine is running in interrupt context 96 * at error interrupt level. 97 * Action: 98 * It's responsible for identifying ALL the CRBs that are marked 99 * with error, and process them. 100 * 101 * If you find the CRB that's marked with error, map this to the 102 * reason it caused error, and invoke appropriate error handler. 103 * 104 * XXX Be aware of the information in the context register. 105 * 106 * NOTE: 107 * Use REMOTE_HUB_* macro instead of LOCAL_HUB_* so that the interrupt 108 * handler can be run on any node. (not necessarily the node 109 * corresponding to the hub that encountered error). 110 */ 111 112void hubiio_crb_error_handler(struct hubdev_info *hubdev_info) 113{ 114 nasid_t nasid; 115 ii_icrb0_a_u_t icrba; /* II CRB Register A */ 116 ii_icrb0_b_u_t icrbb; /* II CRB Register B */ 117 ii_icrb0_c_u_t icrbc; /* II CRB Register C */ 118 ii_icrb0_d_u_t icrbd; /* II CRB Register D */ 119 ii_icrb0_e_u_t icrbe; /* II CRB Register D */ 120 int i; 121 int num_errors = 0; /* Num of errors handled */ 122 ioerror_t ioerror; 123 124 nasid = hubdev_info->hdi_nasid; 125 126 /* 127 * XXX - Add locking for any recovery actions 128 */ 129 /* 130 * Scan through all CRBs in the Hub, and handle the errors 131 * in any of the CRBs marked. 132 */ 133 for (i = 0; i < IIO_NUM_CRBS; i++) { 134 /* Check this crb entry to see if it is in error. */ 135 icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(nasid, IIO_ICRB_B(i)); 136 137 if (icrbb.b_mark == 0) { 138 continue; 139 } 140 141 icrba.ii_icrb0_a_regval = REMOTE_HUB_L(nasid, IIO_ICRB_A(i)); 142 143 IOERROR_INIT(&ioerror); 144 145 /* read other CRB error registers. */ 146 icrbc.ii_icrb0_c_regval = REMOTE_HUB_L(nasid, IIO_ICRB_C(i)); 147 icrbd.ii_icrb0_d_regval = REMOTE_HUB_L(nasid, IIO_ICRB_D(i)); 148 icrbe.ii_icrb0_e_regval = REMOTE_HUB_L(nasid, IIO_ICRB_E(i)); 149 150 IOERROR_SETVALUE(&ioerror, errortype, icrbb.b_ecode); 151 152 /* Check if this error is due to BTE operation, 153 * and handle it separately. 154 */ 155 if (icrbd.d_bteop || 156 ((icrbb.b_initiator == IIO_ICRB_INIT_BTE0 || 157 icrbb.b_initiator == IIO_ICRB_INIT_BTE1) && 158 (icrbb.b_imsgtype == IIO_ICRB_IMSGT_BTE || 159 icrbb.b_imsgtype == IIO_ICRB_IMSGT_SN1NET))) { 160 161 int bte_num; 162 163 if (icrbd.d_bteop) 164 bte_num = icrbc.c_btenum; 165 else /* b_initiator bit 2 gives BTE number */ 166 bte_num = (icrbb.b_initiator & 0x4) >> 2; 167 168 hubiio_crb_free(hubdev_info, i); 169 170 bte_crb_error_handler(nasid_to_cnodeid(nasid), bte_num, 171 i, &ioerror, icrbd.d_bteop); 172 num_errors++; 173 continue; 174 } 175 } 176} 177 178/* 179 * Function : hub_error_init 180 * Purpose : initialize the error handling requirements for a given hub. 181 * Parameters : cnode, the compact nodeid. 182 * Assumptions : Called only once per hub, either by a local cpu. Or by a 183 * remote cpu, when this hub is headless.(cpuless) 184 * Returns : None 185 */ 186void hub_error_init(struct hubdev_info *hubdev_info) 187{ 188 189 if (request_irq(SGI_II_ERROR, hub_eint_handler, IRQF_SHARED, 190 "SN_hub_error", hubdev_info)) { 191 printk(KERN_ERR "hub_error_init: Failed to request_irq for 0x%p\n", 192 hubdev_info); 193 return; 194 } 195 irq_set_handler(SGI_II_ERROR, handle_level_irq); 196 sn_set_err_irq_affinity(SGI_II_ERROR); 197} 198 199 200/* 201 * Function : ice_error_init 202 * Purpose : initialize the error handling requirements for a given tio. 203 * Parameters : cnode, the compact nodeid. 204 * Assumptions : Called only once per tio. 205 * Returns : None 206 */ 207void ice_error_init(struct hubdev_info *hubdev_info) 208{ 209 210 if (request_irq 211 (SGI_TIO_ERROR, (void *)hub_eint_handler, IRQF_SHARED, "SN_TIO_error", 212 (void *)hubdev_info)) { 213 printk("ice_error_init: request_irq() error hubdev_info 0x%p\n", 214 hubdev_info); 215 return; 216 } 217 irq_set_handler(SGI_TIO_ERROR, handle_level_irq); 218 sn_set_err_irq_affinity(SGI_TIO_ERROR); 219} 220 221