aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/sn/io/sn2/shubio.c
blob: 90294319bf09dcd4d46f0faf68b48ca1792ed9d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
/* $Id: shubio.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $
 *
 * 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-2003 Silicon Graphics, Inc. All rights reserved.
 */


#include <linux/types.h>
#include <linux/slab.h>
#include <asm/smp.h>
#include <asm/sn/sgi.h>
#include <asm/sn/io.h>
#include <asm/sn/iograph.h>
#include <asm/sn/invent.h>
#include <asm/sn/hcl.h>
#include <asm/sn/labelcl.h>
#include <asm/sn/sn_private.h>
#include <asm/sn/klconfig.h>
#include <asm/sn/sn_cpuid.h>
#include <asm/sn/pci/pciio.h>
#include <asm/sn/pci/pcibr.h>
#include <asm/sn/xtalk/xtalk.h>
#include <asm/sn/pci/pcibr_private.h>
#include <asm/sn/intr.h>
#include <asm/sn/ioerror_handling.h>
#include <asm/sn/ioerror.h>
#include <asm/sn/sn2/shubio.h>


error_state_t error_state_get(devfs_handle_t v);
error_return_code_t error_state_set(devfs_handle_t v,error_state_t new_state);


/*
 * Get the xtalk provider function pointer for the
 * specified hub.
 */

/*ARGSUSED*/
int
hub_xp_error_handler(
	devfs_handle_t 	hub_v, 
	nasid_t		nasid, 
	int		error_code, 
	ioerror_mode_t	mode, 
	ioerror_t	*ioerror)
{
	/*REFERENCED*/
	hubreg_t	iio_imem;
	devfs_handle_t	xswitch;
	error_state_t	e_state;
	cnodeid_t	cnode;

	/*
	 * Before walking down to the next level, check if
	 * the I/O link is up. If it's been disabled by the 
	 * hub ii for some reason, we can't even touch the
	 * widget registers.
	 */
	iio_imem = REMOTE_HUB_L(nasid, IIO_IMEM);

	if (!(iio_imem & (IIO_IMEM_B0ESD|IIO_IMEM_W0ESD))){
		/* 
		 * IIO_IMEM_B0ESD getting set, indicates II shutdown
		 * on HUB0 parts.. Hopefully that's not true for 
		 * Hub1 parts..
		 *
		 *
		 * If either one of them is shut down, can't
		 * go any further.
		 */
		return IOERROR_XTALKLEVEL;
	}

	/* Get the error state of the hub */
	e_state = error_state_get(hub_v);

	cnode = NASID_TO_COMPACT_NODEID(nasid);

	xswitch = NODEPDA(cnode)->basew_xc;

	/* Set the error state of the crosstalk device to that of
	 * hub.
	 */
	if (error_state_set(xswitch , e_state) == 
	    ERROR_RETURN_CODE_CANNOT_SET_STATE)
		return(IOERROR_UNHANDLED);

	/* Clean the error state of the hub if we are in the action handling
	 * phase.
	 */
	if (e_state == ERROR_STATE_ACTION)
		(void)error_state_set(hub_v, ERROR_STATE_NONE);
	/* hand the error off to the switch or the directly
	 * connected crosstalk device.
	 */
	return  xtalk_error_handler(xswitch,
				    error_code, mode, ioerror);

}

/* 
 * Check if the widget in error has been enabled for PIO accesses
 */
int
is_widget_pio_enabled(ioerror_t *ioerror)
{
	cnodeid_t	src_node;
	nasid_t		src_nasid;
	hubreg_t	ii_iowa;
	xwidgetnum_t	widget;
	iopaddr_t	p;

	/* Get the node where the PIO error occurred */
	IOERROR_GETVALUE(p,ioerror, srcnode);
	src_node = p;
	if (src_node == CNODEID_NONE)
		return(0);

	/* Get the nasid for the cnode */
	src_nasid = COMPACT_TO_NASID_NODEID(src_node);
	if (src_nasid == INVALID_NASID)
		return(0);

	/* Read the Outbound widget access register for this hub */
	ii_iowa = REMOTE_HUB_L(src_nasid, IIO_IOWA);
	IOERROR_GETVALUE(p,ioerror, widgetnum);
	widget = p;

	/* Check if the PIOs to the widget with PIO error have been
	 * enabled.
	 */
	if (ii_iowa & IIO_IOWA_WIDGET(widget))
		return(1);

	return(0);
}

/*
 * Hub IO error handling.
 *
 *	Gets invoked for different types of errors found at the hub. 
 *	Typically this includes situations from bus error or due to 
 *	an error interrupt (mostly generated at the hub).
 */
int
hub_ioerror_handler(
	devfs_handle_t 	hub_v, 
	int		error_code,
	int		mode,
	struct io_error_s	*ioerror)
{
	hubinfo_t 	hinfo; 		/* Hub info pointer */
	nasid_t		nasid;
	int		retval = 0;
	/*REFERENCED*/
	iopaddr_t 	p;

	IOERROR_DUMP("hub_ioerror_handler", error_code, mode, ioerror);

	hubinfo_get(hub_v, &hinfo);

	if (!hinfo){
		/* Print an error message and return */
		goto end;
	}
	nasid = hinfo->h_nasid;

	switch(error_code) {

	case PIO_READ_ERROR:
		/* 
		 * Cpu got a bus error while accessing IO space.
		 * hubaddr field in ioerror structure should have
		 * the IO address that caused access error.
		 */

		/*
		 * Identify if  the physical address in hub_error_data
		 * corresponds to small/large window, and accordingly,
		 * get the xtalk address.
		 */

		/*
		 * Evaluate the widget number and the widget address that
		 * caused the error. Use 'vaddr' if it's there.
		 * This is typically true either during probing
		 * or a kernel driver getting into trouble. 
		 * Otherwise, use paddr to figure out widget details
		 * This is typically true for user mode bus errors while
		 * accessing I/O space.
		 */
		 IOERROR_GETVALUE(p,ioerror,vaddr);
		 if (p){
		    /* 
		     * If neither in small window nor in large window range,
		     * outright reject it.
		     */
		    IOERROR_GETVALUE(p,ioerror,vaddr);
		    if (NODE_SWIN_ADDR(nasid, (paddr_t)p)){
			iopaddr_t	hubaddr;
			xwidgetnum_t	widgetnum;
			iopaddr_t	xtalkaddr;

			IOERROR_GETVALUE(p,ioerror,hubaddr);
			hubaddr = p;
			widgetnum = SWIN_WIDGETNUM(hubaddr);
			xtalkaddr = SWIN_WIDGETADDR(hubaddr);
			/* 
			 * differentiate local register vs IO space access
			 */
			IOERROR_SETVALUE(ioerror,widgetnum,widgetnum);
			IOERROR_SETVALUE(ioerror,xtalkaddr,xtalkaddr);


		    } else if (NODE_BWIN_ADDR(nasid, (paddr_t)p)){
			/* 
			 * Address corresponds to large window space. 
			 * Convert it to xtalk address.
			 */
			int		bigwin;
			hub_piomap_t    bw_piomap;
			xtalk_piomap_t	xt_pmap = NULL;
			iopaddr_t	hubaddr;
			xwidgetnum_t	widgetnum;
			iopaddr_t	xtalkaddr;

			IOERROR_GETVALUE(p,ioerror,hubaddr);
			hubaddr = p;

			/*
			 * Have to loop to find the correct xtalk_piomap 
			 * because the're not allocated on a one-to-one
			 * basis to the window number.
			 */
			for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) {
				bw_piomap = hubinfo_bwin_piomap_get(hinfo,
								    bigwin);

				if (bw_piomap->hpio_bigwin_num ==
				    (BWIN_WINDOWNUM(hubaddr) - 1)) {
					xt_pmap = hub_piomap_xt_piomap(bw_piomap);
					break;
				}
			}

			ASSERT(xt_pmap);

			widgetnum = xtalk_pio_target_get(xt_pmap);
			xtalkaddr = xtalk_pio_xtalk_addr_get(xt_pmap) + BWIN_WIDGETADDR(hubaddr);

			IOERROR_SETVALUE(ioerror,widgetnum,widgetnum);
			IOERROR_SETVALUE(ioerror,xtalkaddr,xtalkaddr);

			/* 
			 * Make sure that widgetnum doesnot map to hub 
			 * register widget number, as we never use
			 * big window to access hub registers. 
			 */
			ASSERT(widgetnum != HUB_REGISTER_WIDGET);
		    }
		} else if (IOERROR_FIELDVALID(ioerror,hubaddr)) {
			iopaddr_t	hubaddr;
			xwidgetnum_t	widgetnum;
			iopaddr_t	xtalkaddr;

			IOERROR_GETVALUE(p,ioerror,hubaddr);
			hubaddr = p;
			if (BWIN_WINDOWNUM(hubaddr)){
				int 	window = BWIN_WINDOWNUM(hubaddr) - 1;
				hubreg_t itte;
				itte = (hubreg_t)HUB_L(IIO_ITTE_GET(nasid, window));
				widgetnum =  (itte >> IIO_ITTE_WIDGET_SHIFT) & 
						IIO_ITTE_WIDGET_MASK;
				xtalkaddr = (((itte >> IIO_ITTE_OFFSET_SHIFT) &
					IIO_ITTE_OFFSET_MASK) << 
					     BWIN_SIZE_BITS) +
					BWIN_WIDGETADDR(hubaddr);
			} else {
				widgetnum = SWIN_WIDGETNUM(hubaddr);
				xtalkaddr = SWIN_WIDGETADDR(hubaddr);
			}
			IOERROR_SETVALUE(ioerror,widgetnum,widgetnum);
			IOERROR_SETVALUE(ioerror,xtalkaddr,xtalkaddr);
		} else {
			IOERROR_DUMP("hub_ioerror_handler", error_code, 
						mode, ioerror);
			IOERR_PRINTF(printk(
				"hub_ioerror_handler: Invalid address passed"));

			return IOERROR_INVALIDADDR;
		}


		IOERROR_GETVALUE(p,ioerror,widgetnum);
		if ((p) == HUB_REGISTER_WIDGET) {
			/* 
			 * Error in accessing Hub local register
			 * This should happen mostly in SABLE mode..
			 */
			retval = 0;
		} else {
			/* Make sure that the outbound widget access for this
			 * widget is enabled.
			 */
			if (!is_widget_pio_enabled(ioerror)) {
				if (error_state_get(hub_v) == 
				    ERROR_STATE_ACTION)
					ioerror_dump("No outbound widget"
						     " access - ", 
						     error_code, mode, ioerror);
				return(IOERROR_HANDLED);
			}
		  

			retval = hub_xp_error_handler(
				hub_v, nasid, error_code, mode, ioerror);

		}

		IOERR_PRINTF(printk(
			"hub_ioerror_handler:PIO_READ_ERROR return: %d",
				retval));

		break;

	case PIO_WRITE_ERROR:
		/*
		 * This hub received an interrupt indicating a widget 
		 * attached to this hub got a timeout. 
		 * widgetnum field should be filled to indicate the
		 * widget that caused error.
		 *
		 * NOTE: This hub may have nothing to do with this error.
		 * We are here since the widget attached to the xbow 
		 * gets its PIOs through this hub.
		 *
		 * There is nothing that can be done at this level. 
		 * Just invoke the xtalk error handling mechanism.
		 */
		IOERROR_GETVALUE(p,ioerror,widgetnum);
		if ((p) == HUB_REGISTER_WIDGET) {
		} else {
			/* Make sure that the outbound widget access for this
			 * widget is enabled.
			 */

			if (!is_widget_pio_enabled(ioerror)) {
				if (error_state_get(hub_v) == 
				    ERROR_STATE_ACTION)
					ioerror_dump("No outbound widget"
						     " access - ", 
						     error_code, mode, ioerror);
				return(IOERROR_HANDLED);
			}
		  
			retval = hub_xp_error_handler(
				hub_v, nasid, error_code, mode, ioerror);
		}
		break;
	
	case DMA_READ_ERROR:
		/* 
		 * DMA Read error always ends up generating an interrupt
		 * at the widget level, and never at the hub level. So,
		 * we don't expect to come here any time
		 */
		ASSERT(0);
		retval = IOERROR_UNHANDLED;
		break;

	case DMA_WRITE_ERROR:
		/*
		 * DMA Write error is generated when a write by an I/O 
		 * device could not be completed. Problem is, device is
		 * totally unaware of this problem, and would continue
		 * writing to system memory. So, hub has a way to send
		 * an error interrupt on the first error, and bitbucket
		 * all further write transactions.
		 * Coming here indicates that hub detected one such error,
		 * and we need to handle it.
		 *
		 * Hub interrupt handler would have extracted physaddr, 
		 * widgetnum, and widgetdevice from the CRB 
		 *
		 * There is nothing special to do here, since gathering
		 * data from crb's is done elsewhere. Just pass the 
		 * error to xtalk layer.
		 */
		retval = hub_xp_error_handler(hub_v, nasid, error_code, mode,
					      ioerror);
		break;
	
	default:
		ASSERT(0);
		return IOERROR_BADERRORCODE;
	
	}
	
	/*
	 * If error was not handled, we may need to take certain action
	 * based on the error code.
	 * For e.g. in case of PIO_READ_ERROR, we may need to release the
	 * PIO Read entry table (they are sticky after errors).
	 * Similarly other cases. 
	 *
	 * Further Action TBD 
	 */
end:	
	if (retval == IOERROR_HWGRAPH_LOOKUP) {
		/*
		 * If we get errors very early, we can't traverse
		 * the path using hardware graph. 
		 * To handle this situation, we need a functions
		 * which don't depend on the hardware graph vertex to 
		 * handle errors. This break the modularity of the
		 * existing code. Instead we print out the reason for
		 * not handling error, and return. On return, all the
		 * info collected would be dumped. This should provide 
		 * sufficient info to analyse the error.
		 */
		printk("Unable to handle IO error: hardware graph not setup\n");
	}

	return retval;
}

#define L_BITSMINOR 18
#define L_MAXMAJ 0x1ff
#define emajor(x) (int )(((unsigned )(x)>>L_BITSMINOR) & L_MAXMAJ)
#define dev_is_vertex(dev) (emajor((dev_t)(dev)) == 0)

#define INFO_LBL_ERROR_STATE    "error_state"

#define v_error_state_get(v,s)                                          \
(hwgraph_info_get_LBL(v,INFO_LBL_ERROR_STATE, (arbitrary_info_t *)&s))

#define v_error_state_set(v,s,replace)                                  \
(replace ?                                                              \
hwgraph_info_replace_LBL(v,INFO_LBL_ERROR_STATE,(arbitrary_info_t)s,0) :\
hwgraph_info_add_LBL(v,INFO_LBL_ERROR_STATE, (arbitrary_info_t)s))


#define v_error_state_clear(v)                                          \
(hwgraph_info_remove_LBL(v,INFO_LBL_ERROR_STATE,0))

/*
 * error_state_get
 *              Get the state of the vertex.
 *              Returns ERROR_STATE_INVALID on failure
 *                      current state otherwise
 */
error_state_t
error_state_get(devfs_handle_t v)
{
        error_state_t   s;

        /* Check if we have a valid hwgraph vertex */
        if (!dev_is_vertex(v))
                return(ERROR_STATE_NONE);

        /* Get the labelled info hanging off the vertex which corresponds
         * to the state.
         */
        if (v_error_state_get(v, s) != GRAPH_SUCCESS) {
                return(ERROR_STATE_NONE);
        }
        return(s);
}


/*
 * error_state_set
 *              Set the state of the vertex
 *              Returns ERROR_RETURN_CODE_CANNOT_SET_STATE on failure
 *                      ERROR_RETURN_CODE_SUCCESS otherwise
 */
error_return_code_t
error_state_set(devfs_handle_t v,error_state_t new_state)
{
        error_state_t   old_state;
        boolean_t       replace = B_TRUE;

        /* Check if we have a valid hwgraph vertex */
        if (!dev_is_vertex(v))
                return(ERROR_RETURN_CODE_GENERAL_FAILURE);


        /* This means that the error state needs to be cleaned */
        if (new_state == ERROR_STATE_NONE) {
                /* Make sure that we have an error state */
                if (v_error_state_get(v,old_state) == GRAPH_SUCCESS)
                        v_error_state_clear(v);
                return(ERROR_RETURN_CODE_SUCCESS);
        }

        /* Check if the state information has been set at least once
         * for this vertex.
         */
        if (v_error_state_get(v,old_state) != GRAPH_SUCCESS)
                replace = B_FALSE;

        if (v_error_state_set(v,new_state,replace) != GRAPH_SUCCESS) {
                return(ERROR_RETURN_CODE_CANNOT_SET_STATE);
        }
        return(ERROR_RETURN_CODE_SUCCESS);
}