File: /usr/src/linux/arch/ia64/sn/io/eeprom.c

1     /*
2      *
3      * This file is subject to the terms and conditions of the GNU General Public
4      * License.  See the file "COPYING" in the main directory of this archive
5      * for more details.
6      *
7      * Copyright (C) 2000 Silicon Graphics, Inc.
8      * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com)
9      */
10     
11     
12     /*
13      * WARNING:     There is more than one copy of this file in different isms.
14      *              All copies must be kept exactly in sync.
15      *              Do not modify this file without also updating the following:
16      *
17      *              irix/kern/io/eeprom.c
18      *              stand/arcs/lib/libsk/ml/eeprom.c
19      *		stand/arcs/lib/libkl/io/eeprom.c
20      *
21      *      (from time to time they might not be in sync but that's due to bringup
22      *       activity - this comment is to remind us that they eventually have to
23      *       get back together)
24      *
25      * eeprom.c
26      *
27      * access to board-mounted EEPROMs via the L1 system controllers
28      *
29      */
30     
31     /**************************************************************************
32      *                                                                        *
33      *  Copyright (C) 1999 Silicon Graphics, Inc.                             *
34      *                                                                        *
35      *  These coded instructions, statements, and computer programs  contain  *
36      *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
37      *  are protected by Federal copyright law.  They  may  not be disclosed  *
38      *  to  third  parties  or copied or duplicated in any form, in whole or  *
39      *  in part, without the prior written consent of Silicon Graphics, Inc.  *
40      *                                                                        *
41      **************************************************************************
42      */
43     
44     
45     #include <linux/types.h>
46     #include <linux/config.h>
47     #include <linux/slab.h>
48     #include <asm/sn/sgi.h>
49     #include <asm/sn/iograph.h>
50     #include <asm/sn/invent.h>
51     #include <asm/sn/hcl.h>
52     #include <asm/sn/hcl_util.h>
53     #include <asm/sn/labelcl.h>
54     #include <asm/sn/eeprom.h>
55     #include <asm/sn/ksys/i2c.h>
56     /* #include <sys/SN/SN1/ip27log.h> */
57     #include <asm/sn/router.h>
58     #include <asm/sn/module.h>
59     #include <asm/sn/ksys/l1.h>
60     #include <asm/sn/nodepda.h>
61     #include <asm/sn/clksupport.h>
62     
63     #if defined(EEPROM_DEBUG)
64     #define db_printf(x) printk x
65     #else
66     #define db_printf(x) printk x
67     #endif
68     
69     #define BCOPY(x,y,z)	memcpy(y,x,z)
70     
71     #define UNDERSCORE	0	/* don't convert underscores to hyphens */
72     #define HYPHEN		1	/* convert underscores to hyphens */
73     
74     void		copy_ascii_field( char *to, char *from, int length,
75     			          int change_underscore );
76     uint64_t	generate_unique_id( char *sn, int sn_len );
77     uchar_t		char_to_base36( char c );
78     int		nicify( char *dst, eeprom_brd_record_t *src );
79     static void	int64_to_hex_string( char *out, uint64_t val );
80     
81     // extern int router_lock( net_vec_t, int, int );
82     // extern int router_unlock( net_vec_t );
83     #define ROUTER_LOCK(p) 	// router_lock(p, 10000, 3000000)
84     #define ROUTER_UNLOCK(p) 	// router_unlock(p)
85     
86     #define IP27LOG_OVNIC           "OverrideNIC"
87     
88     
89     /* the following function converts an EEPROM record to a close facsimile
90      * of the string returned by reading a Dallas Semiconductor NIC (see
91      * one of the many incarnations of nic.c for details on that driver)
92      */
93     int nicify( char *dst, eeprom_brd_record_t *src )
94     {
95         int field_len;
96         uint64_t unique_id;
97         char *cur_dst = dst;
98         eeprom_board_ia_t   *board;
99     
100         board   = src->board_ia;
101         ASSERT( board );  /* there should always be a board info area */
102     
103         /* copy part number */
104         strcpy( cur_dst, "Part:" );
105         cur_dst += strlen( cur_dst );
106         ASSERT( (board->part_num_tl & FIELD_FORMAT_MASK)
107     	    == FIELD_FORMAT_ASCII );
108         field_len = board->part_num_tl & FIELD_LENGTH_MASK;
109         copy_ascii_field( cur_dst, board->part_num, field_len, HYPHEN );
110         cur_dst += field_len;
111     
112         /* copy product name */
113         strcpy( cur_dst, ";Name:" );
114         cur_dst += strlen( cur_dst );
115         ASSERT( (board->product_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII );
116         field_len = board->product_tl & FIELD_LENGTH_MASK;
117         copy_ascii_field( cur_dst, board->product, field_len, UNDERSCORE );
118         cur_dst += field_len;
119     
120         /* copy serial number */
121         strcpy( cur_dst, ";Serial:" );
122         cur_dst += strlen( cur_dst );
123         ASSERT( (board->serial_num_tl & FIELD_FORMAT_MASK)
124     	    == FIELD_FORMAT_ASCII );
125         field_len = board->serial_num_tl & FIELD_LENGTH_MASK;
126         copy_ascii_field( cur_dst, board->serial_num, field_len,
127     		      HYPHEN);
128     
129         cur_dst += field_len;
130     
131         /* copy revision */
132         strcpy( cur_dst, ";Revision:");
133         cur_dst += strlen( cur_dst );
134         ASSERT( (board->board_rev_tl & FIELD_FORMAT_MASK)
135     	    == FIELD_FORMAT_ASCII );
136         field_len = board->board_rev_tl & FIELD_LENGTH_MASK;
137         copy_ascii_field( cur_dst, board->board_rev, field_len, HYPHEN );
138         cur_dst += field_len;
139     
140         /* EEPROMs don't have equivalents for the Group, Capability and
141          * Variety fields, so we pad these with 0's
142          */
143         strcpy( cur_dst, ";Group:ff;Capability:ffffffff;Variety:ff" );
144         cur_dst += strlen( cur_dst );
145     
146         /* use the board serial number to "fake" a laser id */
147         strcpy( cur_dst, ";Laser:" );
148         cur_dst += strlen( cur_dst );
149         unique_id = generate_unique_id( board->serial_num,
150     				    board->serial_num_tl & FIELD_LENGTH_MASK );
151         int64_to_hex_string( cur_dst, unique_id );
152         strcat( dst, ";" );
153     
154         return 1;
155     }
156     
157     
158     /* These functions borrow heavily from chars2* in nic.c
159      */
160     void copy_ascii_field( char *to, char *from, int length,
161     		       int change_underscore )
162     {
163         int i;
164         for( i = 0; i < length; i++ ) {
165     
166     	/* change underscores to hyphens if requested */
167     	if( from[i] == '_' && change_underscore == HYPHEN )
168     	    to[i] = '-';
169     
170     	/* ; and ; are separators, so mustn't appear within
171     	 * a field */
172     	else if( from[i] == ':' || from[i] == ';' )
173     	    to[i] = '?';
174     
175     	/* I'm not sure why or if ASCII character 0xff would
176     	 * show up in an EEPROM field, but the NIC parsing
177     	 * routines wouldn't like it if it did... so we
178     	 * get rid of it, just in case. */
179     	else if( (unsigned char)from[i] == (unsigned char)0xff )
180     	    to[i] = ' ';
181     	
182     	/* unprintable characters are replaced with . */
183     	else if( from[i] < ' ' || from[i] >= 0x7f )
184     	    to[i] = '.';
185     
186     	/* otherwise, just copy the character */
187     	else
188     	    to[i] = from[i];
189         }
190     
191         if( i == 0 ) {
192     	to[i] = ' '; /* return at least a space... */
193     	i++;
194         }
195         to[i] = 0;	     /* terminating null */
196     }
197     
198     /* Note that int64_to_hex_string currently only has a big-endian
199      * implementation.
200      */
201     #ifdef _MIPSEB
202     static void int64_to_hex_string( char *out, uint64_t val )
203     {
204         int i;
205         uchar_t table[] = "0123456789abcdef";
206         uchar_t *byte_ptr = (uchar_t *)&val;
207         for( i = 0; i < sizeof(uint64_t); i++ ) {
208     	out[i*2] = table[ ((*byte_ptr) >> 4) & 0x0f ];
209     	out[i*2+1] = table[ (*byte_ptr) & 0x0f ];
210     	byte_ptr++;
211         }
212         out[i*2] = '\0';
213     }
214     
215     #else /* little endian */
216     
217     static void int64_to_hex_string( char *out, uint64_t val )
218     {
219     
220     
221     	printk("int64_to_hex_string needs a little-endian implementation.\n");
222     }
223     #endif /* _MIPSEB */
224     
225     /* Convert a standard ASCII serial number to a unique integer
226      * id number by treating the serial number string as though
227      * it were a base 36 number
228      */
229     uint64_t generate_unique_id( char *sn, int sn_len )
230     {
231         int uid = 0;
232         int i;
233     
234         #define VALID_BASE36(c)	((c >= '0' && c <='9') \
235     			    ||   (c >= 'A' && c <='Z') \
236     			    ||   (c >= 'a' && c <='z'))
237     
238         for( i = 0; i < sn_len; i++ ) {
239     	if( !VALID_BASE36(sn[i]) )
240     	    continue;
241     	uid *= 36;
242     	uid += char_to_base36( sn[i] );
243         }
244     
245         if( uid == 0 )
246     	return rtc_time();
247     
248         return uid;
249     }
250     
251     uchar_t char_to_base36( char c )
252     {
253         uchar_t val;
254     
255         if( c >= '0' && c <= '9' )
256     	val = (c - '0');
257     
258         else if( c >= 'A' && c <= 'Z' )
259     	val = (c - 'A' + 10);
260     
261         else if( c >= 'a' && c <= 'z' )
262     	val = (c - 'a' + 10);
263     
264         else val = 0;
265     
266         return val;
267     }
268     
269     
270     /* given a pointer to the three-byte little-endian EEPROM representation
271      * of date-of-manufacture, this function translates to a big-endian
272      * integer format
273      */
274     int eeprom_xlate_board_mfr_date( uchar_t *src )
275     {
276         int rval = 0;
277         rval += *src; src++;
278         rval += ((int)(*src) << 8); src ++;
279         rval += ((int)(*src) << 16);
280         return rval;
281     }
282     
283     
284     int eeprom_str( char *nic_str, nasid_t nasid, int component )
285     {
286         eeprom_brd_record_t eep;
287         eeprom_board_ia_t board;
288         eeprom_chassis_ia_t chassis;
289         int r;
290     
291         if( (component & C_DIMM) == C_DIMM ) {
292     	/* this function isn't applicable to DIMMs */
293     	return EEP_PARAM;
294         }
295         else {
296     	eep.board_ia = &board;
297     	eep.spd = NULL;
298     	if( !(component & SUBORD_MASK) )
299     	    eep.chassis_ia = &chassis;  /* only main boards have a chassis
300     					 * info area */
301     	else
302     	    eep.chassis_ia = NULL;
303         }
304         
305         switch( component & BRICK_MASK ) {
306           case C_BRICK:
307     	r = cbrick_eeprom_read( &eep, nasid, component );
308     	break;
309           case IO_BRICK:
310     	r = iobrick_eeprom_read( &eep, nasid, component );
311     	break;
312           default:
313     	return EEP_PARAM;  /* must be an invalid component */
314         }
315         if( r )
316     	return r;
317         if( !nicify( nic_str, &eep ) )
318     	return EEP_NICIFY;
319     
320         return EEP_OK;
321     }
322     
323     int vector_eeprom_str( char *nic_str, nasid_t nasid,
324     		       int component, net_vec_t path )
325     {
326         eeprom_brd_record_t eep;
327         eeprom_board_ia_t board;
328         eeprom_chassis_ia_t chassis;
329         int r;
330     
331         eep.board_ia = &board;
332         if( !(component & SUBORD_MASK) )
333             eep.chassis_ia = &chassis;  /* only main boards have a chassis
334                                          * info area */
335         else
336             eep.chassis_ia = NULL;
337     
338         if( !(component & VECTOR) )
339     	return EEP_PARAM;
340     
341         if( (r = vector_eeprom_read( &eep, nasid, path, component )) )
342     	return r;
343     
344         if( !nicify( nic_str, &eep ) )
345             return EEP_NICIFY;
346     
347         return EEP_OK;
348     }
349     
350     
351     int is_iobrick( int nasid, int widget_num )
352     {
353         uint32_t wid_reg;
354         int part_num, mfg_num;
355     
356         /* Read the widget's WIDGET_ID register to get
357          * its part number and mfg number
358          */
359         wid_reg = *(volatile int32_t *)
360             (NODE_SWIN_BASE( nasid, widget_num ) + WIDGET_ID);
361     
362         part_num = (wid_reg & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT;
363         mfg_num = (wid_reg & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT;
364     
365         /* Is this the "xbow part" of an XBridge?  If so, this
366          * widget is definitely part of an I/O brick.
367          */
368         if( part_num == XXBOW_WIDGET_PART_NUM &&
369     	mfg_num == XXBOW_WIDGET_MFGR_NUM )
370     
371     	return 1;
372     
373         /* Is this a "bridge part" of an XBridge?  If so, once
374          * again, we know this widget is part of an I/O brick.
375          */
376         if( part_num == XBRIDGE_WIDGET_PART_NUM &&
377     	mfg_num == XBRIDGE_WIDGET_MFGR_NUM )
378     
379     	return 1;
380     
381         return 0;
382     }
383     
384     
385     int cbrick_uid_get( nasid_t nasid, uint64_t *uid )
386     {
387     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
388         return EEP_L1;
389     #else
390         char uid_str[32];
391         char msg[BRL1_QSIZE];
392         int subch, len;
393         l1sc_t sc;
394         l1sc_t *scp;
395         int local = (nasid == get_nasid());
396     
397         if ( IS_RUNNING_ON_SIMULATOR() )
398     	return EEP_L1;
399     
400         /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
401          * use that value for the cbrick UID rather than the EEPROM
402          * serial number.
403          */
404     #ifdef LOG_GETENV
405         if( ip27log_getenv( nasid, IP27LOG_OVNIC, uid_str, NULL, 0 ) >= 0 )
406         {
407     	/* We successfully read IP27LOG_OVNIC, so return it as the UID. */
408     	db_printf(( "cbrick_uid_get:"
409     		    "Overriding UID with environment variable %s\n", 
410     		    IP27LOG_OVNIC ));
411     	*uid = strtoull( uid_str, NULL, 0 );
412     	return EEP_OK;
413         }
414     #endif
415     
416         /* If this brick is retrieving its own uid, use the local l1sc_t to
417          * arbitrate access to the l1; otherwise, set up a new one.
418          */
419         if( local ) {
420     	scp = get_l1sc();
421         }
422         else {
423     	scp = &sc;
424     	sc_init( &sc, nasid, BRL1_LOCALUART );
425         }
426     
427         /* fill in msg with the opcode & params */
428         BZERO( msg, BRL1_QSIZE );
429         if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
430     	return EEP_L1;
431     
432         if( (len = sc_construct_msg( scp, subch, msg, BRL1_QSIZE,
433     				 L1_ADDR_TASK_GENERAL,
434     				 L1_REQ_SER_NUM, 0 )) < 0 )
435         {
436     	sc_close( scp, subch );
437     	return( EEP_L1 );
438         }
439     
440         /* send the request to the L1 */
441         if( sc_command( scp, subch, msg, msg, &len ) ) {
442     	sc_close( scp, subch );
443     	return( EEP_L1 );
444         }
445     
446         /* free up subchannel */
447         sc_close(scp, subch);
448     
449         /* check response */
450         if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
451         {
452     	return( EEP_L1 );
453         }
454     
455         *uid = generate_unique_id( uid_str, strlen( uid_str ) );
456     
457         return EEP_OK;
458     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
459     }
460     
461     
462     int rbrick_uid_get( nasid_t nasid, net_vec_t path, uint64_t *uid )
463     {
464     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
465         return EEP_L1;
466     #else
467         char uid_str[32];
468         char msg[BRL1_QSIZE];
469         int subch, len;
470         l1sc_t sc;
471     
472         if ( IS_RUNNING_ON_SIMULATOR() )
473     	return EEP_L1;
474     
475     #ifdef BRINGUP
476     #define FAIL								\
477         {									\
478     	*uid = rtc_time();						\
479     	printk( "rbrick_uid_get failed; using current time as uid\n" );	\
480     	return EEP_OK;							\
481         }
482     #endif /* BRINGUP */
483     
484         ROUTER_LOCK(path);
485         sc_init( &sc, nasid, path );
486     
487         /* fill in msg with the opcode & params */
488         BZERO( msg, BRL1_QSIZE );
489         if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 ) {
490     	ROUTER_UNLOCK(path);
491     	FAIL;
492         }
493     
494         if( (len = sc_construct_msg( &sc, subch, msg, BRL1_QSIZE,
495     				 L1_ADDR_TASK_GENERAL,
496     				 L1_REQ_SER_NUM, 0 )) < 0 )
497         {
498     	ROUTER_UNLOCK(path);
499     	sc_close( &sc, subch );
500     	FAIL;
501         }
502     
503         /* send the request to the L1 */
504         if( sc_command( &sc, subch, msg, msg, &len ) ) {
505     	ROUTER_UNLOCK(path);
506     	sc_close( &sc, subch );
507     	FAIL;
508         }
509     
510         /* free up subchannel */
511         ROUTER_UNLOCK(path);
512         sc_close(&sc, subch);
513     
514         /* check response */
515         if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
516         {
517     	FAIL;
518         }
519     
520         *uid = generate_unique_id( uid_str, strlen( uid_str ) );
521     
522         return EEP_OK;
523     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
524     }
525     
526     int iobrick_uid_get( nasid_t nasid, uint64_t *uid )
527     {
528         eeprom_brd_record_t eep;
529         eeprom_board_ia_t board;
530         eeprom_chassis_ia_t chassis;
531         int r;
532     
533         eep.board_ia = &board;
534         eep.chassis_ia = &chassis;
535         eep.spd = NULL;
536     
537         r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
538         if( r != EEP_OK ) {
539             *uid = rtc_time();
540             return r;
541         }
542     
543         *uid = generate_unique_id( board.serial_num,
544                                    board.serial_num_tl & FIELD_LENGTH_MASK );
545     
546         return EEP_OK;
547     }
548     
549     
550     int ibrick_mac_addr_get( nasid_t nasid, char *eaddr )
551     {
552         eeprom_brd_record_t eep;
553         eeprom_board_ia_t board;
554         eeprom_chassis_ia_t chassis;
555         int r;
556         char *tmp;
557     
558         eep.board_ia = &board;
559         eep.chassis_ia = &chassis;
560         eep.spd = NULL;
561     
562         r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
563         if( (r != EEP_OK) || (board.mac_addr[0] == '\0') ) {
564     	db_printf(( "ibrick_mac_addr_get: "
565     		    "Couldn't read MAC address from EEPROM\n" ));
566     	return EEP_L1;
567         }
568         else {
569     	/* successfully read info area */
570     	int ix;
571     	tmp = board.mac_addr;
572     	for( ix = 0; ix < (board.mac_addr_tl & FIELD_LENGTH_MASK); ix++ )
573     	{
574     	    *eaddr++ = *tmp++;
575     	}
576     	*eaddr = '\0';
577         }
578     
579         return EEP_OK;
580     }
581     
582     
583     /* 
584      * eeprom_vertex_info_set
585      *
586      * Given a vertex handle, a component designation, a starting nasid
587      * and (in the case of a router) a vector path to the component, this
588      * function will read the EEPROM and attach the resulting information
589      * to the vertex in the same string format as that provided by the
590      * Dallas Semiconductor NIC drivers.  If the vertex already has the
591      * string, this function just returns the string.
592      */
593     
594     extern char *nic_vertex_info_get( devfs_handle_t );
595     extern void nic_vmc_check( devfs_handle_t, char * );
596     #ifdef BRINGUP
597     /* the following were lifted from nic.c - change later? */
598     #define MAX_INFO 2048
599     #define NEWSZ(ptr,sz)   ((ptr) = kern_malloc((sz)))
600     #define DEL(ptr) (kern_free((ptr)))
601     #endif /* BRINGUP */
602     
603     char *eeprom_vertex_info_set( int component, int nasid, devfs_handle_t v,
604                                   net_vec_t path )
605     {
606             char *info_tmp;
607             int info_len;
608             char *info;
609     
610             /* see if this vertex is already marked */
611             info_tmp = nic_vertex_info_get(v);
612             if (info_tmp) return info_tmp;
613     
614             /* get a temporary place for the data */
615             NEWSZ(info_tmp, MAX_INFO);
616             if (!info_tmp) return NULL;
617     
618             /* read the EEPROM */
619     	if( component & R_BRICK ) {
620     	    if( RBRICK_EEPROM_STR( info_tmp, nasid, path ) != EEP_OK )
621     		return NULL;
622     	}
623     	else {
624                 if( eeprom_str( info_tmp, nasid, component ) != EEP_OK )
625     	        return NULL;
626     	}
627     
628             /* allocate a smaller final place */
629             info_len = strlen(info_tmp)+1;
630             NEWSZ(info, info_len);
631             if (info) {
632                     strcpy(info, info_tmp);
633                     DEL(info_tmp);
634             } else {
635                     info = info_tmp;
636             }
637     
638             /* add info to the vertex */
639             hwgraph_info_add_LBL(v, INFO_LBL_NIC,
640                                  (arbitrary_info_t) info);
641     
642             /* see if someone else got there first */
643             info_tmp = nic_vertex_info_get(v);
644             if (info != info_tmp) {
645                 DEL(info);
646                 return info_tmp;
647             }
648     
649             /* export the data */
650             hwgraph_info_export_LBL(v, INFO_LBL_NIC, info_len);
651     
652             /* trigger all matching callbacks */
653             nic_vmc_check(v, info);
654     
655             return info;
656     }
657     
658     
659     /*********************************************************************
660      *
661      * stubs for use until the Bedrock/L1 link is available
662      *
663      */
664     
665     #include <asm/sn/nic.h>
666     
667     /* #define EEPROM_TEST */
668     
669     /* fake eeprom reading functions (replace when the BR/L1 communication
670      * channel is in working order)
671      */
672     
673     
674     /* generate a charater in [0-9A-Z]; if an "extra" character is
675      * specified (such as '_'), include it as one of the possibilities.
676      */
677     char random_eeprom_ch( char extra ) 
678     {
679         char ch;
680         int modval = 36;
681         if( extra )
682     	modval++;
683         
684         ch = rtc_time() % modval;
685     
686         if( ch < 10 )
687             ch += '0';
688         else if( ch >= 10 && ch < 36 )
689     	ch += ('A' - 10);
690         else
691     	ch = extra;
692     
693         return ch;
694     }
695     
696     /* create a part number of the form xxx-xxxx-xxx.
697      * It may be important later to generate different
698      * part numbers depending on the component we're
699      * supposed to be "reading" from, so the component
700      * paramter is provided.
701      */
702     void fake_a_part_number( char *buf, int component )
703     {
704         int i;
705         switch( component ) {
706     
707         /* insert component-specific routines here */
708     
709         case C_BRICK:
710     	strcpy( buf, "030-1266-001" );
711     	break;
712         default:
713             for( i = 0; i < 12; i++ ) {
714     	    if( i == 3 || i == 8 )
715     	        buf[i] = '-';
716     	    else
717     	        buf[i] = random_eeprom_ch(0);
718             }
719         }
720     }
721     
722     
723     /* create a six-character serial number */
724     void fake_a_serial_number( char *buf, uint64_t ser )
725     {
726         int i;
727         static const char hexchars[] = "0123456789ABCDEF";
728     
729         if (ser) {
730     	for( i = 5; i >=0; i-- ) {
731     	    buf[i] = hexchars[ser & 0xf];
732     	    ser >>= 4;
733     	}
734         }
735         else {
736     	for( i = 0; i < 6; i++ )
737     	    buf[i] = random_eeprom_ch(0);
738         }
739     }
740     
741     
742     void fake_a_product_name( uchar_t *format, char* buf, int component )
743     {
744         switch( component & BRICK_MASK ) {
745     
746         case C_BRICK:
747     	if( component & SUBORD_MASK ) {
748     	    strcpy( buf, "C_BRICK_SUB" );
749     	    *format = 0xCB;
750     	}
751     	else {
752     	    strcpy( buf, "IP35" );
753     	    *format = 0xC4;
754     	}
755     	break;
756     
757         case R_BRICK:
758             if( component & SUBORD_MASK ) {
759                 strcpy( buf, "R_BRICK_SUB" );
760                 *format = 0xCB;
761             }
762             else {
763                 strcpy( buf, "R_BRICK" );
764                 *format = 0xC7;
765             }
766             break;
767     
768         case IO_BRICK:
769             if( component & SUBORD_MASK ) {
770                 strcpy( buf, "IO_BRICK_SUB" );
771                 *format = 0xCC;
772             }
773             else {
774                 strcpy( buf, "IO_BRICK" );
775                 *format = 0xC8;
776             }
777             break;
778     
779         default:
780     	strcpy( buf, "UNK_DEVICE" );
781     	*format = 0xCA;
782         }
783     }
784     
785     
786     
787     int fake_an_eeprom_record( eeprom_brd_record_t *buf, int component, 
788     			   uint64_t ser )
789     {
790         eeprom_board_ia_t *board;
791         eeprom_chassis_ia_t *chassis;
792         int i, cs;
793     
794         board = buf->board_ia;
795         chassis = buf->chassis_ia;
796     
797         if( !(component & SUBORD_MASK) ) {
798     	if( !chassis )
799     	    return EEP_PARAM;
800     	chassis->format = 0;
801     	chassis->length = 5;
802     	chassis->type = 0x17;
803     
804     	chassis->part_num_tl = 0xCC;
805     	fake_a_part_number( chassis->part_num, component );
806     	chassis->serial_num_tl = 0xC6;
807     	fake_a_serial_number( chassis->serial_num, ser );
808     
809     	cs = chassis->format + chassis->length + chassis->type
810     	    + chassis->part_num_tl + chassis->serial_num_tl;
811     	for( i = 0; i < (chassis->part_num_tl & FIELD_LENGTH_MASK); i++ )
812     	    cs += chassis->part_num[i];
813     	for( i = 0; i < (chassis->serial_num_tl & FIELD_LENGTH_MASK); i++ )
814     	    cs += chassis->serial_num[i];
815     	chassis->checksum = 256 - (cs % 256);
816         }
817     
818         if( !board )
819     	return EEP_PARAM;
820         board->format = 0;
821         board->length = 10;
822         board->language = 0;
823         board->mfg_date = 1789200; /* noon, 5/26/99 */
824         board->manuf_tl = 0xC3;
825         strcpy( board->manuf, "SGI" );
826     
827         fake_a_product_name( &(board->product_tl), board->product, component );
828     
829         board->serial_num_tl = 0xC6;
830         fake_a_serial_number( board->serial_num, ser );
831     
832         board->part_num_tl = 0xCC;
833         fake_a_part_number( board->part_num, component );
834     
835         board->board_rev_tl = 0xC2;
836         board->board_rev[0] = '0';
837         board->board_rev[1] = '1';
838     
839         board->eeprom_size_tl = 0x01;
840         board->eeprom_size = 1;
841     
842         board->temp_waiver_tl = 0xC2;
843         board->temp_waiver[0] = '0';
844         board->temp_waiver[1] = '1';
845     
846         cs = board->format + board->length + board->language
847     	+ (board->mfg_date & 0xFF)
848     	+ (board->mfg_date & 0xFF00)
849     	+ (board->mfg_date & 0xFF0000)
850     	+ board->manuf_tl + board->product_tl + board->serial_num_tl
851     	+ board->part_num_tl + board->board_rev_tl
852     	+ board->board_rev[0] + board->board_rev[1]
853     	+ board->eeprom_size_tl + board->eeprom_size + board->temp_waiver_tl
854     	+ board->temp_waiver[0] + board->temp_waiver[1];
855         for( i = 0; i < (board->manuf_tl & FIELD_LENGTH_MASK); i++ )
856     	cs += board->manuf[i];
857         for( i = 0; i < (board->product_tl & FIELD_LENGTH_MASK); i++ )
858     	cs += board->product[i];
859         for( i = 0; i < (board->serial_num_tl & FIELD_LENGTH_MASK); i++ )
860     	cs += board->serial_num[i];
861         for( i = 0; i < (board->part_num_tl & FIELD_LENGTH_MASK); i++ )
862     	cs += board->part_num[i];
863         
864         board->checksum = 256 - (cs % 256);
865     
866         return EEP_OK;
867     }
868     
869     #define EEPROM_CHUNKSIZE	64
870     
871     #if defined(EEPROM_DEBUG)
872     #define RETURN_ERROR							\
873     {									\
874         printk( "read_ia error return, component 0x%x, line %d"		\
875     	    ", address 0x%x, ia code 0x%x\n",				\
876     	    l1_compt, __LINE__, sc->subch[subch].target, ia_code );	\
877         return EEP_L1;							\
878     }
879     
880     #else
881     #define RETURN_ERROR	return(EEP_L1)
882     #endif
883     
884     int read_ia( l1sc_t *sc, int subch, int l1_compt, 
885     	     int ia_code, char *eep_record )
886     {
887     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
888         return EEP_L1;
889     #else
890         char msg[BRL1_QSIZE]; 	   /* message buffer */
891         int len;              	   /* number of bytes used in message buffer */
892         int ia_len = EEPROM_CHUNKSIZE; /* remaining bytes in info area */
893         int offset = 0;                /* current offset into info area */
894     
895         if ( IS_RUNNING_ON_SIMULATOR() )
896     	return EEP_L1;
897     
898         BZERO( msg, BRL1_QSIZE );
899     
900         /* retrieve EEPROM data in 64-byte chunks
901          */
902     
903         while( ia_len )
904         {
905     	/* fill in msg with opcode & params */
906     	if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
907     				     L1_ADDR_TASK_GENERAL,
908     				     L1_REQ_EEPROM, 8,
909     				     L1_ARG_INT, l1_compt,
910     				     L1_ARG_INT, ia_code,
911     				     L1_ARG_INT, offset,
912     				     L1_ARG_INT, ia_len )) < 0 )
913     	{
914     	    RETURN_ERROR;
915     	}
916     
917     	/* send the request to the L1 */
918     
919     	if( sc_command( sc, subch, msg, msg, &len ) ) {
920     	    RETURN_ERROR;
921     	}
922     
923     	/* check response */
924     	if( sc_interpret_resp( msg, 5, 
925     			       L1_ARG_INT, &ia_len,
926     			       L1_ARG_UNKNOWN, &len, eep_record ) < 0 )
927     	{
928     	    RETURN_ERROR;
929     	}
930     
931     	if( ia_len > EEPROM_CHUNKSIZE )
932     	    ia_len = EEPROM_CHUNKSIZE;
933     
934     	eep_record += EEPROM_CHUNKSIZE;
935     	offset += EEPROM_CHUNKSIZE;
936         }
937     
938         return EEP_OK;
939     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
940     }
941     
942     
943     int read_spd( l1sc_t *sc, int subch, int l1_compt,
944     	      eeprom_spd_u *spd )
945     {
946     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
947         return EEP_L1;
948     #else
949         char msg[BRL1_QSIZE]; 	    /* message buffer */
950         int len;              	    /* number of bytes used in message buffer */
951         int resp;                       /* l1 response code */
952         int spd_len = EEPROM_CHUNKSIZE; /* remaining bytes in spd record */
953         int offset = 0;		    /* current offset into spd record */
954         char *spd_p = spd->bytes;	    /* "thumb" for writing to spd */
955     
956         if ( IS_RUNNING_ON_SIMULATOR() )
957     	return EEP_L1;
958     
959         BZERO( msg, BRL1_QSIZE );
960     
961         /* retrieve EEPROM data in 64-byte chunks
962          */
963     
964         while( spd_len )
965         {
966     	/* fill in msg with opcode & params */
967     	if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
968     				     L1_ADDR_TASK_GENERAL,
969     				     L1_REQ_EEPROM, 8,
970     				     L1_ARG_INT, l1_compt,
971     				     L1_ARG_INT, L1_EEP_SPD,
972     				     L1_ARG_INT, offset,
973     				     L1_ARG_INT, spd_len )) < 0 )
974     	{
975     	    return( EEP_L1 );
976     	}
977     
978     	/* send the request to the L1 */
979     	if( sc_command( sc, subch, msg, msg, &len ) ) {
980     	    return( EEP_L1 );
981     	}
982     
983     	/* check response */
984     	if( (resp = sc_interpret_resp( msg, 5, 
985     			       L1_ARG_INT, &spd_len,
986     			       L1_ARG_UNKNOWN, &len, spd_p )) < 0 )
987     	{
988                 /*
989                  * translate l1 response code to eeprom.c error codes:
990                  * The L1 response will be L1_RESP_NAVAIL if the spd
991                  * can't be read (i.e. the spd isn't physically there). It will
992                  * return L1_RESP_INVAL if the spd exists, but fails the checksum
993                  * test because the eeprom wasn't programmed, programmed incorrectly,
994                  * or corrupted. L1_RESP_NAVAIL indicates the eeprom is likely not present,
995                  * whereas L1_RESP_INVAL indicates the eeprom is present, but the data is
996                  * invalid.
997                  */
998                 if(resp == L1_RESP_INVAL) {
999                     resp = EEP_BAD_CHECKSUM;
1000                 } else {
1001                     resp = EEP_L1;
1002                 }
1003                 return( resp );
1004     	}
1005     
1006     	if( spd_len > EEPROM_CHUNKSIZE )
1007     	    spd_len = EEPROM_CHUNKSIZE;
1008     
1009     	spd_p += EEPROM_CHUNKSIZE;
1010     	offset += EEPROM_CHUNKSIZE;
1011         }
1012         return EEP_OK;
1013     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
1014     }
1015     
1016     
1017     int read_chassis_ia( l1sc_t *sc, int subch, int l1_compt,
1018     		     eeprom_chassis_ia_t *ia )
1019     {
1020         char eep_record[512];          /* scratch area for building up info area */
1021         char *eep_rec_p = eep_record;  /* thumb for moving through eep_record */
1022         int checksum = 0;              /* use to verify eeprom record checksum */
1023         int i;
1024     
1025         /* Read in info area record from the L1.
1026          */
1027         if( read_ia( sc, subch, l1_compt, L1_EEP_CHASSIS, eep_record )
1028     	!= EEP_OK )
1029         {
1030     	return EEP_L1;
1031         }
1032     
1033         /* Now we've got the whole info area.  Transfer it to the data structure.
1034          */
1035     
1036         eep_rec_p = eep_record;
1037         ia->format = *eep_rec_p++;
1038         ia->length = *eep_rec_p++;
1039         if( ia->length == 0 ) {
1040     	/* since we're using 8*ia->length-1 as an array index later, make
1041     	 * sure it's sane.
1042     	 */
1043     	db_printf(( "read_chassis_ia: eeprom length byte of ZERO\n" ));
1044     	return EEP_L1;
1045         }
1046         ia->type = *eep_rec_p++;
1047        
1048         ia->part_num_tl = *eep_rec_p++;
1049     
1050         (void)BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
1051         eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
1052     
1053         ia->serial_num_tl = *eep_rec_p++;
1054     
1055         BCOPY( eep_rec_p, ia->serial_num, 
1056     	   (ia->serial_num_tl & FIELD_LENGTH_MASK) );
1057         eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
1058     
1059         ia->checksum = eep_record[(8 * ia->length) - 1];
1060     
1061         /* verify checksum */
1062         eep_rec_p = eep_record;
1063         checksum = 0;
1064         for( i = 0; i < (8 * ia->length); i++ ) {
1065     	checksum += *eep_rec_p++;
1066         }
1067     
1068         if( (checksum & 0xff) != 0 )
1069         {
1070     	db_printf(( "read_chassis_ia: bad checksum\n" ));
1071     	db_printf(( "read_chassis_ia: target 0x%x  uart 0x%x\n",
1072     			   sc->subch[subch].target, sc->uart ));
1073     	return EEP_BAD_CHECKSUM;
1074         }
1075     
1076         return EEP_OK;
1077     }
1078     
1079     
1080     int read_board_ia( l1sc_t *sc, int subch, int l1_compt,
1081     		   eeprom_board_ia_t *ia )
1082     {
1083         char eep_record[512];          /* scratch area for building up info area */
1084         char *eep_rec_p = eep_record;  /* thumb for moving through eep_record */
1085         int checksum = 0;              /* running checksum total */
1086         int i;
1087     
1088         BZERO( ia, sizeof( eeprom_board_ia_t ) );
1089     
1090         /* Read in info area record from the L1.
1091          */
1092         if( read_ia( sc, subch, l1_compt, L1_EEP_BOARD, eep_record )
1093     	!= EEP_OK )
1094         {
1095     	db_printf(( "read_board_ia: error reading info area from L1\n" ));
1096     	return EEP_L1;
1097         }
1098     
1099          /* Now we've got the whole info area.  Transfer it to the data structure.
1100           */
1101     
1102         eep_rec_p = eep_record;
1103         ia->format = *eep_rec_p++;
1104         ia->length = *eep_rec_p++;
1105         if( ia->length == 0 ) {
1106     	/* since we're using 8*ia->length-1 as an array index later, make
1107     	 * sure it's sane.
1108     	 */
1109     	db_printf(( "read_board_ia: eeprom length byte of ZERO\n" ));
1110     	return EEP_L1;
1111         }
1112         ia->language = *eep_rec_p++;
1113         
1114         ia->mfg_date = eeprom_xlate_board_mfr_date( (uchar_t *)eep_rec_p );
1115         eep_rec_p += 3;
1116     
1117         ia->manuf_tl = *eep_rec_p++;
1118         
1119         BCOPY( eep_rec_p, ia->manuf, (ia->manuf_tl & FIELD_LENGTH_MASK) );
1120         eep_rec_p += (ia->manuf_tl & FIELD_LENGTH_MASK);
1121     
1122         ia->product_tl = *eep_rec_p++;
1123         
1124         BCOPY( eep_rec_p, ia->product, (ia->product_tl & FIELD_LENGTH_MASK) );
1125         eep_rec_p += (ia->product_tl & FIELD_LENGTH_MASK);
1126     
1127         ia->serial_num_tl = *eep_rec_p++;
1128         
1129         BCOPY(eep_rec_p, ia->serial_num, (ia->serial_num_tl & FIELD_LENGTH_MASK));
1130         eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
1131     
1132         ia->part_num_tl = *eep_rec_p++;
1133     
1134         BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
1135         eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
1136     
1137         eep_rec_p++; /* we do not use the FRU file id */
1138         
1139         ia->board_rev_tl = *eep_rec_p++;
1140         
1141         BCOPY( eep_rec_p, ia->board_rev, (ia->board_rev_tl & FIELD_LENGTH_MASK) );
1142         eep_rec_p += (ia->board_rev_tl & FIELD_LENGTH_MASK);
1143     
1144         ia->eeprom_size_tl = *eep_rec_p++;
1145         ia->eeprom_size = *eep_rec_p++;
1146     
1147         ia->temp_waiver_tl = *eep_rec_p++;
1148         
1149         BCOPY( eep_rec_p, ia->temp_waiver, 
1150     	   (ia->temp_waiver_tl & FIELD_LENGTH_MASK) );
1151         eep_rec_p += (ia->temp_waiver_tl & FIELD_LENGTH_MASK);
1152     
1153         /* if there's more, we must be reading a main board; get
1154          * additional fields
1155          */
1156         if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
1157     
1158     	ia->ekey_G_tl = *eep_rec_p++;
1159     	BCOPY( eep_rec_p, (char *)&ia->ekey_G, 
1160     	       ia->ekey_G_tl & FIELD_LENGTH_MASK );
1161     	eep_rec_p += (ia->ekey_G_tl & FIELD_LENGTH_MASK);
1162     	
1163     	ia->ekey_P_tl = *eep_rec_p++;
1164     	BCOPY( eep_rec_p, (char *)&ia->ekey_P, 
1165     	       ia->ekey_P_tl & FIELD_LENGTH_MASK );
1166     	eep_rec_p += (ia->ekey_P_tl & FIELD_LENGTH_MASK);
1167     	
1168     	ia->ekey_Y_tl = *eep_rec_p++;
1169     	BCOPY( eep_rec_p, (char *)&ia->ekey_Y, 
1170     	       ia->ekey_Y_tl & FIELD_LENGTH_MASK );
1171     	eep_rec_p += (ia->ekey_Y_tl & FIELD_LENGTH_MASK);
1172     	
1173     	/* 
1174     	 * need to get a couple more fields if this is an I brick 
1175     	 */
1176     	if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
1177     
1178     	    ia->mac_addr_tl = *eep_rec_p++;
1179     	    BCOPY( eep_rec_p, ia->mac_addr, 
1180     		   ia->mac_addr_tl & FIELD_LENGTH_MASK );
1181     	    eep_rec_p += (ia->mac_addr_tl & FIELD_LENGTH_MASK);
1182     	    
1183     	    ia->ieee1394_cfg_tl = *eep_rec_p++;
1184     	    BCOPY( eep_rec_p, ia->ieee1394_cfg,
1185     		   ia->ieee1394_cfg_tl & FIELD_LENGTH_MASK );
1186     	    
1187     	}
1188         }
1189     
1190         ia->checksum = eep_record[(ia->length * 8) - 1];
1191     
1192         /* verify checksum */
1193         eep_rec_p = eep_record;
1194         checksum = 0;
1195         for( i = 0; i < (8 * ia->length); i++ ) {
1196     	checksum += *eep_rec_p++;
1197         }
1198     
1199         if( (checksum & 0xff) != 0 )
1200         {
1201     	db_printf(( "read_board_ia: bad checksum\n" ));
1202     	db_printf(( "read_board_ia: target 0x%x  uart 0x%x\n",
1203     		    sc->subch[subch].target, sc->uart ));
1204     	return EEP_BAD_CHECKSUM;
1205         }
1206     
1207         return EEP_OK;
1208     }
1209     
1210     
1211     int _cbrick_eeprom_read( eeprom_brd_record_t *buf, l1sc_t *scp,
1212     			 int component )
1213     {
1214     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
1215         return EEP_L1;
1216     #else
1217         int r;
1218         uint64_t uid = 0;
1219     #ifdef LOG_GETENV
1220         char uid_str[32];
1221     #endif
1222         int l1_compt, subch;
1223     
1224         if ( IS_RUNNING_ON_SIMULATOR() )
1225     	return EEP_L1;
1226     
1227         /* make sure we're targeting a cbrick */
1228         if( !(component & C_BRICK) )
1229     	return EEP_PARAM;
1230     
1231         /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
1232          * use that value for the cbrick UID rather than the EEPROM
1233          * serial number.
1234          */
1235     #ifdef LOG_GETENV
1236         if( ip27log_getenv( scp->nasid, IP27LOG_OVNIC, uid_str, "0", 0 ) >= 0 )
1237         {
1238     	db_printf(( "_cbrick_eeprom_read: "
1239     		    "Overriding UID with environment variable %s\n", 
1240     		    IP27LOG_OVNIC ));
1241     	uid = strtoull( uid_str, NULL, 0 );
1242         }
1243     #endif
1244     
1245         if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
1246     	return EEP_L1;
1247     
1248         if((component & C_DIMM) == C_DIMM) {
1249             l1_compt = L1_EEP_DIMM(component & COMPT_MASK);
1250             r = read_spd(scp,subch,l1_compt, buf->spd);
1251             sc_close(scp,subch);
1252             return(r);
1253         }
1254     
1255         switch( component )
1256         {
1257           case C_BRICK:
1258     	/* c-brick motherboard */
1259     	l1_compt = L1_EEP_NODE;
1260     	r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
1261     	if( r != EEP_OK ) {
1262     	    sc_close( scp, subch );
1263     	    db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
1264     	    return fake_an_eeprom_record( buf, component, uid );
1265     	}
1266     	if( uid ) {
1267     	    /* If IP27LOG_OVNIC is set, we want to put that value
1268     	     * in as our UID. */
1269     	    fake_a_serial_number( buf->chassis_ia->serial_num, uid );
1270     	    buf->chassis_ia->serial_num_tl = 6;
1271     	}
1272     	break;
1273     
1274           case C_PIMM:
1275     	/* one of the PIMM boards */
1276     	l1_compt = L1_EEP_PIMM( component & COMPT_MASK );
1277     	break;
1278     
1279           default:
1280     	/* unsupported board type */
1281     	sc_close( scp, subch );
1282     	return EEP_PARAM;
1283         }
1284     	      
1285         r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
1286         sc_close( scp, subch );
1287         if( r != EEP_OK ) 
1288         {
1289     	db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
1290     	return fake_an_eeprom_record( buf, component, uid );
1291         }
1292         return EEP_OK;
1293     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
1294     }
1295     
1296     
1297     int cbrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
1298         		        int component )
1299     {
1300     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
1301         return EEP_L1;
1302     #else
1303         l1sc_t *scp;
1304         int local = (nasid == get_nasid());
1305     
1306         if ( IS_RUNNING_ON_SIMULATOR() )
1307     	return EEP_L1;
1308     
1309         /* If this brick is retrieving its own uid, use the local l1sc_t to
1310          * arbitrate access to the l1; otherwise, set up a new one (prom) or
1311          * use an existing remote l1sc_t (kernel)
1312          */
1313         if( local ) {
1314     	scp = get_l1sc();
1315         }
1316         else {
1317     	scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
1318         }
1319     
1320         return _cbrick_eeprom_read( buf, scp, component );
1321     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
1322     }
1323     
1324     
1325     int iobrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
1326     			 int component )
1327     {
1328     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
1329         return EEP_L1;
1330     #else
1331         int r;
1332         int l1_compt, subch;
1333         l1sc_t *scp;
1334         int local = (nasid == get_nasid());
1335     
1336         if ( IS_RUNNING_ON_SIMULATOR() )
1337     	return EEP_L1;
1338     
1339         /* make sure we're talking to an applicable brick */
1340         if( !(component & IO_BRICK) ) {
1341     	return EEP_PARAM;
1342         }
1343     
1344         /* If we're talking to this c-brick's attached io brick, use
1345          * the local l1sc_t; otherwise, set up a new one (prom) or
1346          * use an existing remote l1sc_t (kernel)
1347          */
1348         if( local ) {
1349     	scp = get_l1sc();
1350         }
1351         else {
1352     	scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
1353         }
1354     
1355         if( (subch = sc_open( scp, L1_ADDR_LOCALIO )) < 0 )
1356     	return EEP_L1;
1357     
1358     
1359         switch( component )
1360         {
1361           case IO_BRICK:
1362     	/* IO brick motherboard */
1363     	l1_compt = L1_EEP_LOGIC;
1364     	r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
1365     
1366     	if( r != EEP_OK ) {
1367     	    sc_close( scp, subch );
1368     	/*
1369     	 * Whenever we no longer need to test on hardware
1370     	 * that does not have EEPROMS, then this can be removed.
1371     	 */
1372     	    r = fake_an_eeprom_record( buf, component, rtc_time() );
1373     	    return r;
1374     	}
1375     	break;
1376     
1377           case IO_POWER:
1378     	/* IO brick power board */
1379     	l1_compt = L1_EEP_POWER;
1380     	break;
1381     
1382           default:
1383     	/* unsupported board type */
1384     	sc_close( scp, subch );
1385     	return EEP_PARAM;
1386         }
1387     
1388         r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
1389         sc_close( scp, subch );
1390         if( r != EEP_OK ) {
1391     	return r;
1392         }
1393         return EEP_OK;
1394     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */    
1395     }
1396     
1397     
1398     int vector_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
1399     			net_vec_t path, int component )
1400     {
1401     #if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL)
1402         return EEP_L1;
1403     #else
1404         int r;
1405         uint64_t uid = 0;
1406         int l1_compt, subch;
1407         l1sc_t sc;
1408     
1409         if ( IS_RUNNING_ON_SIMULATOR() )
1410     	return EEP_L1;
1411     
1412         /* make sure we're targeting an applicable brick */
1413         if( !(component & VECTOR) )
1414     	return EEP_PARAM;
1415     
1416         switch( component & BRICK_MASK )
1417         {
1418           case R_BRICK:
1419     	ROUTER_LOCK( path );
1420     	sc_init( &sc, nasid, path );
1421     
1422     	if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 )
1423     	{
1424     	    db_printf(( "vector_eeprom_read: couldn't open subch\n" ));
1425     	    ROUTER_UNLOCK(path);
1426     	    return EEP_L1;
1427     	}
1428     	switch( component )
1429     	{
1430     	  case R_BRICK:
1431     	    /* r-brick motherboard */
1432     	    l1_compt = L1_EEP_LOGIC;
1433         	    r = read_chassis_ia( &sc, subch, l1_compt, buf->chassis_ia );
1434     	    if( r != EEP_OK ) {
1435     		sc_close( &sc, subch );
1436     		ROUTER_UNLOCK( path );
1437     		printk( "vector_eeprom_read: couldn't get rbrick eeprom info;"
1438     			" using current time as uid\n" );
1439     		uid = rtc_time();
1440     		db_printf(("vector_eeprom_read: using a fake eeprom record\n"));
1441     		return fake_an_eeprom_record( buf, component, uid );
1442     	    }
1443     	    break;
1444     
1445     	  case R_POWER:
1446     	    /* r-brick power board */
1447     	    l1_compt = L1_EEP_POWER;
1448     	    break;
1449     
1450     	  default:
1451     	    /* unsupported board type */
1452     	    sc_close( &sc, subch );
1453     	    ROUTER_UNLOCK( path );
1454     	    return EEP_PARAM;
1455     	}
1456     	r = read_board_ia( &sc, subch, l1_compt, buf->board_ia );
1457     	sc_close( &sc, subch );
1458     	ROUTER_UNLOCK( path );
1459     	if( r != EEP_OK ) {
1460     	    db_printf(( "vector_eeprom_read: using a fake eeprom record\n" ));
1461     	    return fake_an_eeprom_record( buf, component, uid );
1462     	}
1463     	return EEP_OK;
1464     
1465           case C_BRICK:
1466     	sc_init( &sc, nasid, path );
1467     	return _cbrick_eeprom_read( buf, &sc, component );
1468     
1469           default:
1470     	/* unsupported brick type */
1471     	return EEP_PARAM;
1472         }
1473     #endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */
1474     }
1475