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