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 = ≻
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