Merge branch 'reset-seq' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/libat...
[linux-2.6] / arch / ia64 / sn / kernel / huberror.c
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);
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", __FUNCTION__,
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", __FUNCTION__);
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  */
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
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  */
186 void hub_error_init(struct hubdev_info *hubdev_info)
187 {
188         if (request_irq(SGI_II_ERROR, hub_eint_handler, IRQF_SHARED,
189                         "SN_hub_error", (void *)hubdev_info))
190                 printk("hub_error_init: Failed to request_irq for 0x%p\n",
191                     hubdev_info);
192         return;
193 }
194
195
196 /*
197  * Function     : ice_error_init
198  * Purpose      : initialize the error handling requirements for a given tio.
199  * Parameters   : cnode, the compact nodeid.
200  * Assumptions  : Called only once per tio.
201  * Returns      : None
202  */
203 void ice_error_init(struct hubdev_info *hubdev_info)
204 {
205         if (request_irq
206             (SGI_TIO_ERROR, (void *)hub_eint_handler, IRQF_SHARED, "SN_TIO_error",
207              (void *)hubdev_info))
208                 printk("ice_error_init: request_irq() error hubdev_info 0x%p\n",
209                        hubdev_info);
210         return;
211 }
212