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