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
58void		copy_ascii_field( char *to, char *from, int length,
59			          int change_underscore );
60uint64_t	generate_unique_id( char *sn, int sn_len );
61uchar_t		char_to_base36( char c );
62int		nicify( char *dst, eeprom_brd_record_t *src );
63static 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 */
77int 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 */
144void 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
186static 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
201static 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 */
213uint64_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
235uchar_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 */
258int 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
268int 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
307int 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
335int 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
369int cbrick_uid_get( nasid_t nasid, uint64_t *uid )
370{
371    char uid_str[32];
372    char msg[BRL1_QSIZE];
373    int subch, len;
374    l1sc_t sc;
375    l1sc_t *scp;
376    int local = (nasid == get_nasid());
377
378    if ( IS_RUNNING_ON_SIMULATOR() )
379	return EEP_L1;
380
381    /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
382     * use that value for the cbrick UID rather than the EEPROM
383     * serial number.
384     */
385#ifdef LOG_GETENV
386    if( ip27log_getenv( nasid, IP27LOG_OVNIC, uid_str, NULL, 0 ) >= 0 )
387    {
388	/* We successfully read IP27LOG_OVNIC, so return it as the UID. */
389	db_printf(( "cbrick_uid_get:"
390		    "Overriding UID with environment variable %s\n",
391		    IP27LOG_OVNIC ));
392	*uid = strtoull( uid_str, NULL, 0 );
393	return EEP_OK;
394    }
395#endif
396
397    /* If this brick is retrieving its own uid, use the local l1sc_t to
398     * arbitrate access to the l1; otherwise, set up a new one.
399     */
400    if( local ) {
401	scp = get_l1sc();
402    }
403    else {
404	scp = &sc;
405	sc_init( &sc, nasid, BRL1_LOCALHUB_UART );
406    }
407
408    /* fill in msg with the opcode & params */
409    BZERO( msg, BRL1_QSIZE );
410    if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
411	return EEP_L1;
412
413    if( (len = sc_construct_msg( scp, subch, msg, BRL1_QSIZE,
414				 L1_ADDR_TASK_GENERAL,
415				 L1_REQ_SER_NUM, 0 )) < 0 )
416    {
417	sc_close( scp, subch );
418	return( EEP_L1 );
419    }
420
421    /* send the request to the L1 */
422    if( sc_command( scp, subch, msg, msg, &len ) ) {
423	sc_close( scp, subch );
424	return( EEP_L1 );
425    }
426
427    /* free up subchannel */
428    sc_close(scp, subch);
429
430    /* check response */
431    if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
432    {
433	return( EEP_L1 );
434    }
435
436    *uid = generate_unique_id( uid_str, strlen( uid_str ) );
437
438    return EEP_OK;
439}
440
441
442int rbrick_uid_get( nasid_t nasid, net_vec_t path, uint64_t *uid )
443{
444    char uid_str[32];
445    char msg[BRL1_QSIZE];
446    int subch, len;
447    l1sc_t sc;
448
449    if ( IS_RUNNING_ON_SIMULATOR() )
450	return EEP_L1;
451
452#define FAIL								\
453    {									\
454	*uid = rtc_time();						\
455	printk( "rbrick_uid_get failed; using current time as uid\n" );	\
456	return EEP_OK;							\
457    }
458
459    ROUTER_LOCK(path);
460    sc_init( &sc, nasid, path );
461
462    /* fill in msg with the opcode & params */
463    BZERO( msg, BRL1_QSIZE );
464    if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 ) {
465	ROUTER_UNLOCK(path);
466	FAIL;
467    }
468
469    if( (len = sc_construct_msg( &sc, subch, msg, BRL1_QSIZE,
470				 L1_ADDR_TASK_GENERAL,
471				 L1_REQ_SER_NUM, 0 )) < 0 )
472    {
473	ROUTER_UNLOCK(path);
474	sc_close( &sc, subch );
475	FAIL;
476    }
477
478    /* send the request to the L1 */
479    if( sc_command( &sc, subch, msg, msg, &len ) ) {
480	ROUTER_UNLOCK(path);
481	sc_close( &sc, subch );
482	FAIL;
483    }
484
485    /* free up subchannel */
486    ROUTER_UNLOCK(path);
487    sc_close(&sc, subch);
488
489    /* check response */
490    if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 )
491    {
492	FAIL;
493    }
494
495    *uid = generate_unique_id( uid_str, strlen( uid_str ) );
496
497    return EEP_OK;
498}
499
500int iobrick_uid_get( nasid_t nasid, uint64_t *uid )
501{
502    eeprom_brd_record_t eep;
503    eeprom_board_ia_t board;
504    eeprom_chassis_ia_t chassis;
505    int r;
506
507    eep.board_ia = &board;
508    eep.chassis_ia = &chassis;
509    eep.spd = NULL;
510
511    r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
512    if( r != EEP_OK ) {
513        *uid = rtc_time();
514        return r;
515    }
516
517    *uid = generate_unique_id( board.serial_num,
518                               board.serial_num_tl & FIELD_LENGTH_MASK );
519
520    return EEP_OK;
521}
522
523
524int ibrick_mac_addr_get( nasid_t nasid, char *eaddr )
525{
526    eeprom_brd_record_t eep;
527    eeprom_board_ia_t board;
528    eeprom_chassis_ia_t chassis;
529    int r;
530    char *tmp;
531
532    eep.board_ia = &board;
533    eep.chassis_ia = &chassis;
534    eep.spd = NULL;
535
536    r = iobrick_eeprom_read( &eep, nasid, IO_BRICK );
537    if( (r != EEP_OK) || (board.mac_addr[0] == '\0') ) {
538	db_printf(( "ibrick_mac_addr_get: "
539		    "Couldn't read MAC address from EEPROM\n" ));
540	return EEP_L1;
541    }
542    else {
543	/* successfully read info area */
544	int ix;
545	tmp = board.mac_addr;
546	for( ix = 0; ix < (board.mac_addr_tl & FIELD_LENGTH_MASK); ix++ )
547	{
548	    *eaddr++ = *tmp++;
549	}
550	*eaddr = '\0';
551    }
552
553    return EEP_OK;
554}
555
556
557/*
558 * eeprom_vertex_info_set
559 *
560 * Given a vertex handle, a component designation, a starting nasid
561 * and (in the case of a router) a vector path to the component, this
562 * function will read the EEPROM and attach the resulting information
563 * to the vertex in the same string format as that provided by the
564 * Dallas Semiconductor NIC drivers.  If the vertex already has the
565 * string, this function just returns the string.
566 */
567
568extern char *nic_vertex_info_get( devfs_handle_t );
569extern void nic_vmc_check( devfs_handle_t, char * );
570/* the following were lifted from nic.c - change later? */
571#define MAX_INFO 2048
572#define NEWSZ(ptr,sz)   ((ptr) = kern_malloc((sz)))
573#define DEL(ptr) (kern_free((ptr)))
574
575char *eeprom_vertex_info_set( int component, int nasid, devfs_handle_t v,
576                              net_vec_t path )
577{
578        char *info_tmp;
579        int info_len;
580        char *info;
581
582        /* see if this vertex is already marked */
583        info_tmp = nic_vertex_info_get(v);
584        if (info_tmp) return info_tmp;
585
586        /* get a temporary place for the data */
587        NEWSZ(info_tmp, MAX_INFO);
588        if (!info_tmp) return NULL;
589
590        /* read the EEPROM */
591	if( component & R_BRICK ) {
592	    if( RBRICK_EEPROM_STR( info_tmp, nasid, path ) != EEP_OK )
593		return NULL;
594	}
595	else {
596            if( eeprom_str( info_tmp, nasid, component ) != EEP_OK )
597	        return NULL;
598	}
599
600        /* allocate a smaller final place */
601        info_len = strlen(info_tmp)+1;
602        NEWSZ(info, info_len);
603        if (info) {
604                strcpy(info, info_tmp);
605                DEL(info_tmp);
606        } else {
607                info = info_tmp;
608        }
609
610        /* add info to the vertex */
611        hwgraph_info_add_LBL(v, INFO_LBL_NIC,
612                             (arbitrary_info_t) info);
613
614        /* see if someone else got there first */
615        info_tmp = nic_vertex_info_get(v);
616        if (info != info_tmp) {
617            DEL(info);
618            return info_tmp;
619        }
620
621        /* export the data */
622        hwgraph_info_export_LBL(v, INFO_LBL_NIC, info_len);
623
624        /* trigger all matching callbacks */
625        nic_vmc_check(v, info);
626
627        return info;
628}
629
630
631/*********************************************************************
632 *
633 * stubs for use until the Bedrock/L1 link is available
634 *
635 */
636
637#include <asm/sn/nic.h>
638
639/* #define EEPROM_TEST */
640
641/* fake eeprom reading functions (replace when the BR/L1 communication
642 * channel is in working order)
643 */
644
645
646/* generate a charater in [0-9A-Z]; if an "extra" character is
647 * specified (such as '_'), include it as one of the possibilities.
648 */
649char random_eeprom_ch( char extra )
650{
651    char ch;
652    int modval = 36;
653    if( extra )
654	modval++;
655
656    ch = rtc_time() % modval;
657
658    if( ch < 10 )
659        ch += '0';
660    else if( ch >= 10 && ch < 36 )
661	ch += ('A' - 10);
662    else
663	ch = extra;
664
665    return ch;
666}
667
668void fake_a_part_number( char *buf, int component )
669{
670    int i;
671    switch( component ) {
672
673    /* insert component-specific routines here */
674
675    case C_BRICK:
676	strcpy( buf, "030-1266-001" );
677	break;
678    default:
679        for( i = 0; i < 12; i++ ) {
680	    if( i == 3 || i == 8 )
681	        buf[i] = '-';
682	    else
683	        buf[i] = random_eeprom_ch(0);
684        }
685    }
686}
687
688
689/* create a six-character serial number */
690void fake_a_serial_number( char *buf, uint64_t ser )
691{
692    int i;
693    static const char hexchars[] = "0123456789ABCDEF";
694
695    if (ser) {
696	for( i = 5; i >=0; i-- ) {
697	    buf[i] = hexchars[ser & 0xf];
698	    ser >>= 4;
699	}
700    }
701    else {
702	for( i = 0; i < 6; i++ )
703	    buf[i] = random_eeprom_ch(0);
704    }
705}
706
707
708void fake_a_product_name( uchar_t *format, char* buf, int component )
709{
710    switch( component & BRICK_MASK ) {
711
712    case C_BRICK:
713	if( component & SUBORD_MASK ) {
714	    strcpy( buf, "C_BRICK_SUB" );
715	    *format = 0xCB;
716	}
717	else {
718	    strcpy( buf, "IP35" );
719	    *format = 0xC4;
720	}
721	break;
722
723    case R_BRICK:
724        if( component & SUBORD_MASK ) {
725            strcpy( buf, "R_BRICK_SUB" );
726            *format = 0xCB;
727        }
728        else {
729            strcpy( buf, "R_BRICK" );
730            *format = 0xC7;
731        }
732        break;
733
734    case IO_BRICK:
735        if( component & SUBORD_MASK ) {
736            strcpy( buf, "IO_BRICK_SUB" );
737            *format = 0xCC;
738        }
739        else {
740            strcpy( buf, "IO_BRICK" );
741            *format = 0xC8;
742        }
743        break;
744
745    default:
746	strcpy( buf, "UNK_DEVICE" );
747	*format = 0xCA;
748    }
749}
750
751
752
753int fake_an_eeprom_record( eeprom_brd_record_t *buf, int component,
754			   uint64_t ser )
755{
756    eeprom_board_ia_t *board;
757    eeprom_chassis_ia_t *chassis;
758    int i, cs;
759
760    board = buf->board_ia;
761    chassis = buf->chassis_ia;
762
763    if( !(component & SUBORD_MASK) ) {
764	if( !chassis )
765	    return EEP_PARAM;
766	chassis->format = 0;
767	chassis->length = 5;
768	chassis->type = 0x17;
769
770	chassis->part_num_tl = 0xCC;
771	fake_a_part_number( chassis->part_num, component );
772	chassis->serial_num_tl = 0xC6;
773	fake_a_serial_number( chassis->serial_num, ser );
774
775	cs = chassis->format + chassis->length + chassis->type
776	    + chassis->part_num_tl + chassis->serial_num_tl;
777	for( i = 0; i < (chassis->part_num_tl & FIELD_LENGTH_MASK); i++ )
778	    cs += chassis->part_num[i];
779	for( i = 0; i < (chassis->serial_num_tl & FIELD_LENGTH_MASK); i++ )
780	    cs += chassis->serial_num[i];
781	chassis->checksum = 256 - (cs % 256);
782    }
783
784    if( !board )
785	return EEP_PARAM;
786    board->format = 0;
787    board->length = 10;
788    board->language = 0;
789    board->mfg_date = 1789200; /* noon, 5/26/99 */
790    board->manuf_tl = 0xC3;
791    strcpy( board->manuf, "SGI" );
792
793    fake_a_product_name( &(board->product_tl), board->product, component );
794
795    board->serial_num_tl = 0xC6;
796    fake_a_serial_number( board->serial_num, ser );
797
798    board->part_num_tl = 0xCC;
799    fake_a_part_number( board->part_num, component );
800
801    board->board_rev_tl = 0xC2;
802    board->board_rev[0] = '0';
803    board->board_rev[1] = '1';
804
805    board->eeprom_size_tl = 0x01;
806    board->eeprom_size = 1;
807
808    board->temp_waiver_tl = 0xC2;
809    board->temp_waiver[0] = '0';
810    board->temp_waiver[1] = '1';
811
812    cs = board->format + board->length + board->language
813	+ (board->mfg_date & 0xFF)
814	+ (board->mfg_date & 0xFF00)
815	+ (board->mfg_date & 0xFF0000)
816	+ board->manuf_tl + board->product_tl + board->serial_num_tl
817	+ board->part_num_tl + board->board_rev_tl
818	+ board->board_rev[0] + board->board_rev[1]
819	+ board->eeprom_size_tl + board->eeprom_size + board->temp_waiver_tl
820	+ board->temp_waiver[0] + board->temp_waiver[1];
821    for( i = 0; i < (board->manuf_tl & FIELD_LENGTH_MASK); i++ )
822	cs += board->manuf[i];
823    for( i = 0; i < (board->product_tl & FIELD_LENGTH_MASK); i++ )
824	cs += board->product[i];
825    for( i = 0; i < (board->serial_num_tl & FIELD_LENGTH_MASK); i++ )
826	cs += board->serial_num[i];
827    for( i = 0; i < (board->part_num_tl & FIELD_LENGTH_MASK); i++ )
828	cs += board->part_num[i];
829
830    board->checksum = 256 - (cs % 256);
831
832    return EEP_OK;
833}
834
835#define EEPROM_CHUNKSIZE	64
836
837#if defined(EEPROM_DEBUG)
838#define RETURN_ERROR							\
839{									\
840    printk( "read_ia error return, component 0x%x, line %d"		\
841	    ", address 0x%x, ia code 0x%x\n",				\
842	    l1_compt, __LINE__, sc->subch[subch].target, ia_code );	\
843    return EEP_L1;							\
844}
845
846#else
847#define RETURN_ERROR	return(EEP_L1)
848#endif
849
850int read_ia( l1sc_t *sc, int subch, int l1_compt,
851	     int ia_code, char *eep_record )
852{
853    char msg[BRL1_QSIZE]; 	   /* message buffer */
854    int len;              	   /* number of bytes used in message buffer */
855    int ia_len = EEPROM_CHUNKSIZE; /* remaining bytes in info area */
856    int offset = 0;                /* current offset into info area */
857
858    if ( IS_RUNNING_ON_SIMULATOR() )
859	return EEP_L1;
860
861    BZERO( msg, BRL1_QSIZE );
862
863    /* retrieve EEPROM data in 64-byte chunks
864     */
865
866    while( ia_len )
867    {
868	/* fill in msg with opcode & params */
869	if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
870				     L1_ADDR_TASK_GENERAL,
871				     L1_REQ_EEPROM, 8,
872				     L1_ARG_INT, l1_compt,
873				     L1_ARG_INT, ia_code,
874				     L1_ARG_INT, offset,
875				     L1_ARG_INT, ia_len )) < 0 )
876	{
877	    RETURN_ERROR;
878	}
879
880	/* send the request to the L1 */
881
882	if( sc_command( sc, subch, msg, msg, &len ) ) {
883	    RETURN_ERROR;
884	}
885
886	/* check response */
887	if( sc_interpret_resp( msg, 5,
888			       L1_ARG_INT, &ia_len,
889			       L1_ARG_UNKNOWN, &len, eep_record ) < 0 )
890	{
891	    RETURN_ERROR;
892	}
893
894	if( ia_len > EEPROM_CHUNKSIZE )
895	    ia_len = EEPROM_CHUNKSIZE;
896
897	eep_record += EEPROM_CHUNKSIZE;
898	offset += EEPROM_CHUNKSIZE;
899    }
900
901    return EEP_OK;
902}
903
904
905int read_spd( l1sc_t *sc, int subch, int l1_compt,
906	      eeprom_spd_u *spd )
907{
908    char msg[BRL1_QSIZE]; 	    /* message buffer */
909    int len;              	    /* number of bytes used in message buffer */
910    int resp;                       /* l1 response code */
911    int spd_len = EEPROM_CHUNKSIZE; /* remaining bytes in spd record */
912    int offset = 0;		    /* current offset into spd record */
913    char *spd_p = spd->bytes;	    /* "thumb" for writing to spd */
914
915    if ( IS_RUNNING_ON_SIMULATOR() )
916	return EEP_L1;
917
918    BZERO( msg, BRL1_QSIZE );
919
920    /* retrieve EEPROM data in 64-byte chunks
921     */
922
923    while( spd_len )
924    {
925	/* fill in msg with opcode & params */
926	if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE,
927				     L1_ADDR_TASK_GENERAL,
928				     L1_REQ_EEPROM, 8,
929				     L1_ARG_INT, l1_compt,
930				     L1_ARG_INT, L1_EEP_SPD,
931				     L1_ARG_INT, offset,
932				     L1_ARG_INT, spd_len )) < 0 )
933	{
934	    return( EEP_L1 );
935	}
936
937	/* send the request to the L1 */
938	if( sc_command( sc, subch, msg, msg, &len ) ) {
939	    return( EEP_L1 );
940	}
941
942	/* check response */
943	if( (resp = sc_interpret_resp( msg, 5,
944			       L1_ARG_INT, &spd_len,
945			       L1_ARG_UNKNOWN, &len, spd_p )) < 0 )
946	{
947            /*
948             * translate l1 response code to eeprom.c error codes:
949             * The L1 response will be L1_RESP_NAVAIL if the spd
950             * can't be read (i.e. the spd isn't physically there). It will
951             * return L1_RESP_INVAL if the spd exists, but fails the checksum
952             * test because the eeprom wasn't programmed, programmed incorrectly,
953             * or corrupted. L1_RESP_NAVAIL indicates the eeprom is likely not present,
954             * whereas L1_RESP_INVAL indicates the eeprom is present, but the data is
955             * invalid.
956             */
957            if(resp == L1_RESP_INVAL) {
958                resp = EEP_BAD_CHECKSUM;
959            } else {
960                resp = EEP_L1;
961            }
962            return( resp );
963	}
964
965	if( spd_len > EEPROM_CHUNKSIZE )
966	    spd_len = EEPROM_CHUNKSIZE;
967
968	spd_p += EEPROM_CHUNKSIZE;
969	offset += EEPROM_CHUNKSIZE;
970    }
971    return EEP_OK;
972}
973
974
975int read_chassis_ia( l1sc_t *sc, int subch, int l1_compt,
976		     eeprom_chassis_ia_t *ia )
977{
978    char eep_record[512];          /* scratch area for building up info area */
979    char *eep_rec_p = eep_record;  /* thumb for moving through eep_record */
980    int checksum = 0;              /* use to verify eeprom record checksum */
981    int i;
982
983    /* Read in info area record from the L1.
984     */
985    if( read_ia( sc, subch, l1_compt, L1_EEP_CHASSIS, eep_record )
986	!= EEP_OK )
987    {
988	return EEP_L1;
989    }
990
991    /* Now we've got the whole info area.  Transfer it to the data structure.
992     */
993
994    eep_rec_p = eep_record;
995    ia->format = *eep_rec_p++;
996    ia->length = *eep_rec_p++;
997    if( ia->length == 0 ) {
998	/* since we're using 8*ia->length-1 as an array index later, make
999	 * sure it's sane.
1000	 */
1001	db_printf(( "read_chassis_ia: eeprom length byte of ZERO\n" ));
1002	return EEP_L1;
1003    }
1004    ia->type = *eep_rec_p++;
1005
1006    ia->part_num_tl = *eep_rec_p++;
1007
1008    (void)BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
1009    eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
1010
1011    ia->serial_num_tl = *eep_rec_p++;
1012
1013    BCOPY( eep_rec_p, ia->serial_num,
1014	   (ia->serial_num_tl & FIELD_LENGTH_MASK) );
1015    eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
1016
1017    ia->checksum = eep_record[(8 * ia->length) - 1];
1018
1019    /* verify checksum */
1020    eep_rec_p = eep_record;
1021    checksum = 0;
1022    for( i = 0; i < (8 * ia->length); i++ ) {
1023	checksum += *eep_rec_p++;
1024    }
1025
1026    if( (checksum & 0xff) != 0 )
1027    {
1028	db_printf(( "read_chassis_ia: bad checksum\n" ));
1029	db_printf(( "read_chassis_ia: target 0x%x  uart 0x%lx\n",
1030			   sc->subch[subch].target, sc->uart ));
1031	return EEP_BAD_CHECKSUM;
1032    }
1033
1034    return EEP_OK;
1035}
1036
1037
1038int read_board_ia( l1sc_t *sc, int subch, int l1_compt,
1039		   eeprom_board_ia_t *ia )
1040{
1041    char eep_record[512];          /* scratch area for building up info area */
1042    char *eep_rec_p = eep_record;  /* thumb for moving through eep_record */
1043    int checksum = 0;              /* running checksum total */
1044    int i;
1045
1046    BZERO( ia, sizeof( eeprom_board_ia_t ) );
1047
1048    /* Read in info area record from the L1.
1049     */
1050    if( read_ia( sc, subch, l1_compt, L1_EEP_BOARD, eep_record )
1051	!= EEP_OK )
1052    {
1053	db_printf(( "read_board_ia: error reading info area from L1\n" ));
1054	return EEP_L1;
1055    }
1056
1057     /* Now we've got the whole info area.  Transfer it to the data structure.
1058      */
1059
1060    eep_rec_p = eep_record;
1061    ia->format = *eep_rec_p++;
1062    ia->length = *eep_rec_p++;
1063    if( ia->length == 0 ) {
1064	/* since we're using 8*ia->length-1 as an array index later, make
1065	 * sure it's sane.
1066	 */
1067	db_printf(( "read_board_ia: eeprom length byte of ZERO\n" ));
1068	return EEP_L1;
1069    }
1070    ia->language = *eep_rec_p++;
1071
1072    ia->mfg_date = eeprom_xlate_board_mfr_date( (uchar_t *)eep_rec_p );
1073    eep_rec_p += 3;
1074
1075    ia->manuf_tl = *eep_rec_p++;
1076
1077    BCOPY( eep_rec_p, ia->manuf, (ia->manuf_tl & FIELD_LENGTH_MASK) );
1078    eep_rec_p += (ia->manuf_tl & FIELD_LENGTH_MASK);
1079
1080    ia->product_tl = *eep_rec_p++;
1081
1082    BCOPY( eep_rec_p, ia->product, (ia->product_tl & FIELD_LENGTH_MASK) );
1083    eep_rec_p += (ia->product_tl & FIELD_LENGTH_MASK);
1084
1085    ia->serial_num_tl = *eep_rec_p++;
1086
1087    BCOPY(eep_rec_p, ia->serial_num, (ia->serial_num_tl & FIELD_LENGTH_MASK));
1088    eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK);
1089
1090    ia->part_num_tl = *eep_rec_p++;
1091
1092    BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) );
1093    eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK);
1094
1095    eep_rec_p++; /* we do not use the FRU file id */
1096
1097    ia->board_rev_tl = *eep_rec_p++;
1098
1099    BCOPY( eep_rec_p, ia->board_rev, (ia->board_rev_tl & FIELD_LENGTH_MASK) );
1100    eep_rec_p += (ia->board_rev_tl & FIELD_LENGTH_MASK);
1101
1102    ia->eeprom_size_tl = *eep_rec_p++;
1103    ia->eeprom_size = *eep_rec_p++;
1104
1105    ia->temp_waiver_tl = *eep_rec_p++;
1106
1107    BCOPY( eep_rec_p, ia->temp_waiver,
1108	   (ia->temp_waiver_tl & FIELD_LENGTH_MASK) );
1109    eep_rec_p += (ia->temp_waiver_tl & FIELD_LENGTH_MASK);
1110
1111    /* if there's more, we must be reading a main board; get
1112     * additional fields
1113     */
1114    if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
1115
1116	ia->ekey_G_tl = *eep_rec_p++;
1117	BCOPY( eep_rec_p, (char *)&ia->ekey_G,
1118	       ia->ekey_G_tl & FIELD_LENGTH_MASK );
1119	eep_rec_p += (ia->ekey_G_tl & FIELD_LENGTH_MASK);
1120
1121	ia->ekey_P_tl = *eep_rec_p++;
1122	BCOPY( eep_rec_p, (char *)&ia->ekey_P,
1123	       ia->ekey_P_tl & FIELD_LENGTH_MASK );
1124	eep_rec_p += (ia->ekey_P_tl & FIELD_LENGTH_MASK);
1125
1126	ia->ekey_Y_tl = *eep_rec_p++;
1127	BCOPY( eep_rec_p, (char *)&ia->ekey_Y,
1128	       ia->ekey_Y_tl & FIELD_LENGTH_MASK );
1129	eep_rec_p += (ia->ekey_Y_tl & FIELD_LENGTH_MASK);
1130
1131	/*
1132	 * need to get a couple more fields if this is an I brick
1133	 */
1134	if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) {
1135
1136	    ia->mac_addr_tl = *eep_rec_p++;
1137	    BCOPY( eep_rec_p, ia->mac_addr,
1138		   ia->mac_addr_tl & FIELD_LENGTH_MASK );
1139	    eep_rec_p += (ia->mac_addr_tl & FIELD_LENGTH_MASK);
1140
1141	    ia->ieee1394_cfg_tl = *eep_rec_p++;
1142	    BCOPY( eep_rec_p, ia->ieee1394_cfg,
1143		   ia->ieee1394_cfg_tl & FIELD_LENGTH_MASK );
1144
1145	}
1146    }
1147
1148    ia->checksum = eep_record[(ia->length * 8) - 1];
1149
1150    /* verify checksum */
1151    eep_rec_p = eep_record;
1152    checksum = 0;
1153    for( i = 0; i < (8 * ia->length); i++ ) {
1154	checksum += *eep_rec_p++;
1155    }
1156
1157    if( (checksum & 0xff) != 0 )
1158    {
1159	db_printf(( "read_board_ia: bad checksum\n" ));
1160	db_printf(( "read_board_ia: target 0x%x  uart 0x%lx\n",
1161		    sc->subch[subch].target, sc->uart ));
1162	return EEP_BAD_CHECKSUM;
1163    }
1164
1165    return EEP_OK;
1166}
1167
1168
1169int _cbrick_eeprom_read( eeprom_brd_record_t *buf, l1sc_t *scp,
1170			 int component )
1171{
1172    int r;
1173    uint64_t uid = 0;
1174#ifdef LOG_GETENV
1175    char uid_str[32];
1176#endif
1177    int l1_compt, subch;
1178
1179    if ( IS_RUNNING_ON_SIMULATOR() )
1180	return EEP_L1;
1181
1182    /* make sure we're targeting a cbrick */
1183    if( !(component & C_BRICK) )
1184	return EEP_PARAM;
1185
1186    /* If the promlog variable pointed to by IP27LOG_OVNIC is set,
1187     * use that value for the cbrick UID rather than the EEPROM
1188     * serial number.
1189     */
1190#ifdef LOG_GETENV
1191    if( ip27log_getenv( scp->nasid, IP27LOG_OVNIC, uid_str, "0", 0 ) >= 0 )
1192    {
1193	db_printf(( "_cbrick_eeprom_read: "
1194		    "Overriding UID with environment variable %s\n",
1195		    IP27LOG_OVNIC ));
1196	uid = strtoull( uid_str, NULL, 0 );
1197    }
1198#endif
1199
1200    if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 )
1201	return EEP_L1;
1202
1203    if((component & C_DIMM) == C_DIMM) {
1204        l1_compt = L1_EEP_DIMM(component & COMPT_MASK);
1205        r = read_spd(scp,subch,l1_compt, buf->spd);
1206        sc_close(scp,subch);
1207        return(r);
1208    }
1209
1210    switch( component )
1211    {
1212      case C_BRICK:
1213	/* c-brick motherboard */
1214	l1_compt = L1_EEP_NODE;
1215	r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
1216	if( r != EEP_OK ) {
1217	    sc_close( scp, subch );
1218	    db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
1219	    return fake_an_eeprom_record( buf, component, uid );
1220	}
1221	if( uid ) {
1222	    /* If IP27LOG_OVNIC is set, we want to put that value
1223	     * in as our UID. */
1224	    fake_a_serial_number( buf->chassis_ia->serial_num, uid );
1225	    buf->chassis_ia->serial_num_tl = 6;
1226	}
1227	break;
1228
1229      case C_PIMM:
1230	/* one of the PIMM boards */
1231	l1_compt = L1_EEP_PIMM( component & COMPT_MASK );
1232	break;
1233
1234      default:
1235	/* unsupported board type */
1236	sc_close( scp, subch );
1237	return EEP_PARAM;
1238    }
1239
1240    r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
1241    sc_close( scp, subch );
1242    if( r != EEP_OK )
1243    {
1244	db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" ));
1245	return fake_an_eeprom_record( buf, component, uid );
1246    }
1247    return EEP_OK;
1248}
1249
1250
1251int cbrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
1252    		        int component )
1253{
1254    l1sc_t *scp;
1255    int local = (nasid == get_nasid());
1256
1257    if ( IS_RUNNING_ON_SIMULATOR() )
1258	return EEP_L1;
1259
1260    /* If this brick is retrieving its own uid, use the local l1sc_t to
1261     * arbitrate access to the l1; otherwise, set up a new one (prom) or
1262     * use an existing remote l1sc_t (kernel)
1263     */
1264    if( local ) {
1265	scp = get_l1sc();
1266    }
1267    else {
1268	scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
1269    }
1270
1271    return _cbrick_eeprom_read( buf, scp, component );
1272}
1273
1274
1275int iobrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
1276			 int component )
1277{
1278    int r;
1279    int l1_compt, subch;
1280    l1sc_t *scp;
1281    int local = (nasid == get_nasid());
1282
1283    if ( IS_RUNNING_ON_SIMULATOR() )
1284	return EEP_L1;
1285
1286    /* make sure we're talking to an applicable brick */
1287    if( !(component & IO_BRICK) ) {
1288	return EEP_PARAM;
1289    }
1290
1291    /* If we're talking to this c-brick's attached io brick, use
1292     * the local l1sc_t; otherwise, set up a new one (prom) or
1293     * use an existing remote l1sc_t (kernel)
1294     */
1295    if( local ) {
1296	scp = get_l1sc();
1297    }
1298    else {
1299	scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc;
1300    }
1301
1302    if( (subch = sc_open( scp, L1_ADDR_LOCALIO )) < 0 )
1303	return EEP_L1;
1304
1305
1306    switch( component )
1307    {
1308      case IO_BRICK:
1309	/* IO brick motherboard */
1310	l1_compt = L1_EEP_LOGIC;
1311	r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia );
1312
1313	if( r != EEP_OK ) {
1314	    sc_close( scp, subch );
1315	/*
1316	 * Whenever we no longer need to test on hardware
1317	 * that does not have EEPROMS, then this can be removed.
1318	 */
1319	    r = fake_an_eeprom_record( buf, component, rtc_time() );
1320	    return r;
1321	}
1322	break;
1323
1324      case IO_POWER:
1325	/* IO brick power board */
1326	l1_compt = L1_EEP_POWER;
1327	break;
1328
1329      default:
1330	/* unsupported board type */
1331	sc_close( scp, subch );
1332	return EEP_PARAM;
1333    }
1334
1335    r = read_board_ia( scp, subch, l1_compt, buf->board_ia );
1336    sc_close( scp, subch );
1337    if( r != EEP_OK ) {
1338	return r;
1339    }
1340    return EEP_OK;
1341}
1342
1343
1344int vector_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid,
1345			net_vec_t path, int component )
1346{
1347    int r;
1348    uint64_t uid = 0;
1349    int l1_compt, subch;
1350    l1sc_t sc;
1351
1352    if ( IS_RUNNING_ON_SIMULATOR() )
1353	return EEP_L1;
1354
1355    /* make sure we're targeting an applicable brick */
1356    if( !(component & VECTOR) )
1357	return EEP_PARAM;
1358
1359    switch( component & BRICK_MASK )
1360    {
1361      case R_BRICK:
1362	ROUTER_LOCK( path );
1363	sc_init( &sc, nasid, path );
1364
1365	if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 )
1366	{
1367	    db_printf(( "vector_eeprom_read: couldn't open subch\n" ));
1368	    ROUTER_UNLOCK(path);
1369	    return EEP_L1;
1370	}
1371	switch( component )
1372	{
1373	  case R_BRICK:
1374	    /* r-brick motherboard */
1375	    l1_compt = L1_EEP_LOGIC;
1376    	    r = read_chassis_ia( &sc, subch, l1_compt, buf->chassis_ia );
1377	    if( r != EEP_OK ) {
1378		sc_close( &sc, subch );
1379		ROUTER_UNLOCK( path );
1380		printk( "vector_eeprom_read: couldn't get rbrick eeprom info;"
1381			" using current time as uid\n" );
1382		uid = rtc_time();
1383		db_printf(("vector_eeprom_read: using a fake eeprom record\n"));
1384		return fake_an_eeprom_record( buf, component, uid );
1385	    }
1386	    break;
1387
1388	  case R_POWER:
1389	    /* r-brick power board */
1390	    l1_compt = L1_EEP_POWER;
1391	    break;
1392
1393	  default:
1394	    /* unsupported board type */
1395	    sc_close( &sc, subch );
1396	    ROUTER_UNLOCK( path );
1397	    return EEP_PARAM;
1398	}
1399	r = read_board_ia( &sc, subch, l1_compt, buf->board_ia );
1400	sc_close( &sc, subch );
1401	ROUTER_UNLOCK( path );
1402	if( r != EEP_OK ) {
1403	    db_printf(( "vector_eeprom_read: using a fake eeprom record\n" ));
1404	    return fake_an_eeprom_record( buf, component, uid );
1405	}
1406	return EEP_OK;
1407
1408      case C_BRICK:
1409	sc_init( &sc, nasid, path );
1410	return _cbrick_eeprom_read( buf, &sc, component );
1411
1412      default:
1413	/* unsupported brick type */
1414	return EEP_PARAM;
1415    }
1416}
1417