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 
21 void hubiio_crb_error_handler(struct hubdev_info *hubdev_info);
22 extern void bte_crb_error_handler(cnodeid_t, int, int, ioerror_t *,
23 				  int);
hub_eint_handler(int irq,void * arg)24 static 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(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  */
hubiio_crb_free(struct hubdev_info * hubdev_info,int crbnum)69 void 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 
hubiio_crb_error_handler(struct hubdev_info * hubdev_info)112 void 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  */
hub_error_init(struct hubdev_info * hubdev_info)186 void 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  */
ice_error_init(struct hubdev_info * hubdev_info)207 void 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