📄 eeprom.c
字号:
/* * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Jack Steiner (steiner@sgi.com) *//* * WARNING: There is more than one copy of this file in different isms. * All copies must be kept exactly in sync. * Do not modify this file without also updating the following: * * irix/kern/io/eeprom.c * stand/arcs/lib/libsk/ml/eeprom.c * stand/arcs/lib/libkl/io/eeprom.c * * (from time to time they might not be in sync but that's due to bringup * activity - this comment is to remind us that they eventually have to * get back together) * * eeprom.c * * access to board-mounted EEPROMs via the L1 system controllers * *//************************************************************************** * * * Copyright (C) 1999 Silicon Graphics, Inc. * * * * These coded instructions, statements, and computer programs contain * * unpublished proprietary information of Silicon Graphics, Inc., and * * are protected by Federal copyright law. They may not be disclosed * * to third parties or copied or duplicated in any form, in whole or * * in part, without the prior written consent of Silicon Graphics, Inc. * * * ************************************************************************** */#include <linux/types.h>#include <linux/config.h>#include <linux/slab.h>#include <asm/sn/sgi.h>#include <asm/sn/iograph.h>#include <asm/sn/invent.h>#include <asm/sn/hcl.h>#include <asm/sn/hcl_util.h>#include <asm/sn/labelcl.h>#include <asm/sn/eeprom.h>#include <asm/sn/ksys/i2c.h>#include <asm/sn/cmn_err.h>/* #include <sys/SN/SN1/ip27log.h> */#include <asm/sn/router.h>#include <asm/sn/module.h>#include <asm/sn/ksys/l1.h>#include <asm/sn/nodepda.h>#include <asm/sn/clksupport.h>#if defined(EEPROM_DEBUG)#define db_printf(x) printk x#else#define db_printf(x) printk x#endif#define BCOPY(x,y,z) memcpy(y,x,z)#define UNDERSCORE 0 /* don't convert underscores to hyphens */#define HYPHEN 1 /* convert underscores to hyphens */void copy_ascii_field( char *to, char *from, int length, int change_underscore );uint64_t generate_unique_id( char *sn, int sn_len );uchar_t char_to_base36( char c );int nicify( char *dst, eeprom_brd_record_t *src );static void int64_to_hex_string( char *out, uint64_t val );// extern int router_lock( net_vec_t, int, int );// extern int router_unlock( net_vec_t );#define ROUTER_LOCK(p) // router_lock(p, 10000, 3000000)#define ROUTER_UNLOCK(p) // router_unlock(p)#define IP27LOG_OVNIC "OverrideNIC"/* the following function converts an EEPROM record to a close facsimile * of the string returned by reading a Dallas Semiconductor NIC (see * one of the many incarnations of nic.c for details on that driver) */int nicify( char *dst, eeprom_brd_record_t *src ){ int field_len; uint64_t unique_id; char *cur_dst = dst; eeprom_board_ia_t *board; board = src->board_ia; ASSERT( board ); /* there should always be a board info area */ /* copy part number */ strcpy( cur_dst, "Part:" ); cur_dst += strlen( cur_dst ); ASSERT( (board->part_num_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII ); field_len = board->part_num_tl & FIELD_LENGTH_MASK; copy_ascii_field( cur_dst, board->part_num, field_len, HYPHEN ); cur_dst += field_len; /* copy product name */ strcpy( cur_dst, ";Name:" ); cur_dst += strlen( cur_dst ); ASSERT( (board->product_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII ); field_len = board->product_tl & FIELD_LENGTH_MASK; copy_ascii_field( cur_dst, board->product, field_len, UNDERSCORE ); cur_dst += field_len; /* copy serial number */ strcpy( cur_dst, ";Serial:" ); cur_dst += strlen( cur_dst ); ASSERT( (board->serial_num_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII ); field_len = board->serial_num_tl & FIELD_LENGTH_MASK; copy_ascii_field( cur_dst, board->serial_num, field_len, HYPHEN); cur_dst += field_len; /* copy revision */ strcpy( cur_dst, ";Revision:"); cur_dst += strlen( cur_dst ); ASSERT( (board->board_rev_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII ); field_len = board->board_rev_tl & FIELD_LENGTH_MASK; copy_ascii_field( cur_dst, board->board_rev, field_len, HYPHEN ); cur_dst += field_len; /* EEPROMs don't have equivalents for the Group, Capability and * Variety fields, so we pad these with 0's */ strcpy( cur_dst, ";Group:ff;Capability:ffffffff;Variety:ff" ); cur_dst += strlen( cur_dst ); /* use the board serial number to "fake" a laser id */ strcpy( cur_dst, ";Laser:" ); cur_dst += strlen( cur_dst ); unique_id = generate_unique_id( board->serial_num, board->serial_num_tl & FIELD_LENGTH_MASK ); int64_to_hex_string( cur_dst, unique_id ); strcat( dst, ";" ); return 1;}/* These functions borrow heavily from chars2* in nic.c */void copy_ascii_field( char *to, char *from, int length, int change_underscore ){ int i; for( i = 0; i < length; i++ ) { /* change underscores to hyphens if requested */ if( from[i] == '_' && change_underscore == HYPHEN ) to[i] = '-'; /* ; and ; are separators, so mustn't appear within * a field */ else if( from[i] == ':' || from[i] == ';' ) to[i] = '?'; /* I'm not sure why or if ASCII character 0xff would * show up in an EEPROM field, but the NIC parsing * routines wouldn't like it if it did... so we * get rid of it, just in case. */ else if( (unsigned char)from[i] == (unsigned char)0xff ) to[i] = ' '; /* unprintable characters are replaced with . */ else if( from[i] < ' ' || from[i] >= 0x7f ) to[i] = '.'; /* otherwise, just copy the character */ else to[i] = from[i]; } if( i == 0 ) { to[i] = ' '; /* return at least a space... */ i++; } to[i] = 0; /* terminating null */}/* Note that int64_to_hex_string currently only has a big-endian * implementation. */#ifdef _MIPSEBstatic void int64_to_hex_string( char *out, uint64_t val ){ int i; uchar_t table[] = "0123456789abcdef"; uchar_t *byte_ptr = (uchar_t *)&val; for( i = 0; i < sizeof(uint64_t); i++ ) { out[i*2] = table[ ((*byte_ptr) >> 4) & 0x0f ]; out[i*2+1] = table[ (*byte_ptr) & 0x0f ]; byte_ptr++; } out[i*2] = '\0';}#else /* little endian */static void int64_to_hex_string( char *out, uint64_t val ){ printk("int64_to_hex_string needs a little-endian implementation.\n");}#endif /* _MIPSEB *//* Convert a standard ASCII serial number to a unique integer * id number by treating the serial number string as though * it were a base 36 number */uint64_t generate_unique_id( char *sn, int sn_len ){ int uid = 0; int i; #define VALID_BASE36(c) ((c >= '0' && c <='9') \ || (c >= 'A' && c <='Z') \ || (c >= 'a' && c <='z')) for( i = 0; i < sn_len; i++ ) { if( !VALID_BASE36(sn[i]) ) continue; uid *= 36; uid += char_to_base36( sn[i] ); } if( uid == 0 ) return rtc_time(); return uid;}uchar_t char_to_base36( char c ){ uchar_t val; if( c >= '0' && c <= '9' ) val = (c - '0'); else if( c >= 'A' && c <= 'Z' ) val = (c - 'A' + 10); else if( c >= 'a' && c <= 'z' ) val = (c - 'a' + 10); else val = 0; return val;}/* given a pointer to the three-byte little-endian EEPROM representation * of date-of-manufacture, this function translates to a big-endian * integer format */int eeprom_xlate_board_mfr_date( uchar_t *src ){ int rval = 0; rval += *src; src++; rval += ((int)(*src) << 8); src ++; rval += ((int)(*src) << 16); return rval;}int eeprom_str( char *nic_str, nasid_t nasid, int component ){ eeprom_brd_record_t eep; eeprom_board_ia_t board; eeprom_chassis_ia_t chassis; int r; if( (component & C_DIMM) == C_DIMM ) { /* this function isn't applicable to DIMMs */ return EEP_PARAM; } else { eep.board_ia = &board; eep.spd = NULL; if( !(component & SUBORD_MASK) ) eep.chassis_ia = &chassis; /* only main boards have a chassis * info area */ else eep.chassis_ia = NULL; } switch( component & BRICK_MASK ) { case C_BRICK: r = cbrick_eeprom_read( &eep, nasid, component ); break; case IO_BRICK: r = iobrick_eeprom_read( &eep, nasid, component ); break; default: return EEP_PARAM; /* must be an invalid component */ } if( r ) return r; if( !nicify( nic_str, &eep ) ) return EEP_NICIFY; return EEP_OK;}int vector_eeprom_str( char *nic_str, nasid_t nasid, int component, net_vec_t path ){ eeprom_brd_record_t eep; eeprom_board_ia_t board; eeprom_chassis_ia_t chassis; int r; eep.board_ia = &board; if( !(component & SUBORD_MASK) ) eep.chassis_ia = &chassis; /* only main boards have a chassis * info area */ else eep.chassis_ia = NULL; if( !(component & VECTOR) ) return EEP_PARAM; if( (r = vector_eeprom_read( &eep, nasid, path, component )) ) return r; if( !nicify( nic_str, &eep ) ) return EEP_NICIFY; return EEP_OK;}int is_iobrick( int nasid, int widget_num ){ uint32_t wid_reg; int part_num, mfg_num; /* Read the widget's WIDGET_ID register to get * its part number and mfg number */ wid_reg = *(volatile int32_t *) (NODE_SWIN_BASE( nasid, widget_num ) + WIDGET_ID); part_num = (wid_reg & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT; mfg_num = (wid_reg & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT; /* Is this the "xbow part" of an XBridge? If so, this * widget is definitely part of an I/O brick. */ if( part_num == XXBOW_WIDGET_PART_NUM && mfg_num == XXBOW_WIDGET_MFGR_NUM ) return 1; /* Is this a "bridge part" of an XBridge? If so, once * again, we know this widget is part of an I/O brick. */ if( part_num == XBRIDGE_WIDGET_PART_NUM && mfg_num == XBRIDGE_WIDGET_MFGR_NUM ) return 1; return 0;}int cbrick_uid_get( nasid_t nasid, uint64_t *uid ){#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL) return EEP_L1;#else char uid_str[32]; char msg[BRL1_QSIZE]; int subch, len; l1sc_t sc; l1sc_t *scp; int local = (nasid == get_nasid()); if ( IS_RUNNING_ON_SIMULATOR() ) return EEP_L1; /* If the promlog variable pointed to by IP27LOG_OVNIC is set, * use that value for the cbrick UID rather than the EEPROM * serial number. */#ifdef LOG_GETENV if( ip27log_getenv( nasid, IP27LOG_OVNIC, uid_str, NULL, 0 ) >= 0 ) { /* We successfully read IP27LOG_OVNIC, so return it as the UID. */ db_printf(( "cbrick_uid_get:" "Overriding UID with environment variable %s\n", IP27LOG_OVNIC )); *uid = strtoull( uid_str, NULL, 0 ); return EEP_OK; }#endif /* If this brick is retrieving its own uid, use the local l1sc_t to * arbitrate access to the l1; otherwise, set up a new one. */ if( local ) { scp = get_l1sc(); } else { scp = ≻ sc_init( &sc, nasid, BRL1_LOCALUART ); } /* fill in msg with the opcode & params */ BZERO( msg, BRL1_QSIZE ); if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 ) return EEP_L1; if( (len = sc_construct_msg( scp, subch, msg, BRL1_QSIZE, L1_ADDR_TASK_GENERAL, L1_REQ_SER_NUM, 0 )) < 0 ) { sc_close( scp, subch ); return( EEP_L1 ); } /* send the request to the L1 */ if( sc_command( scp, subch, msg, msg, &len ) ) { sc_close( scp, subch ); return( EEP_L1 ); } /* free up subchannel */ sc_close(scp, subch); /* check response */ if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 ) { return( EEP_L1 ); } *uid = generate_unique_id( uid_str, strlen( uid_str ) ); return EEP_OK;#endif /* CONFIG_SERIAL_SGI_L1_PROTOCOL */}int rbrick_uid_get( nasid_t nasid, net_vec_t path, uint64_t *uid ){#if !defined(CONFIG_SERIAL_SGI_L1_PROTOCOL) return EEP_L1;#else char uid_str[32]; char msg[BRL1_QSIZE]; int subch, len; l1sc_t sc; if ( IS_RUNNING_ON_SIMULATOR() ) return EEP_L1;#ifdef BRINGUP#define FAIL \ { \ *uid = rtc_time(); \ printk( "rbrick_uid_get failed; using current time as uid\n" ); \ return EEP_OK; \ }#endif /* BRINGUP */ ROUTER_LOCK(path); sc_init( &sc, nasid, path );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -