diff options
Diffstat (limited to 'board/w7o/vpd.c')
-rw-r--r-- | board/w7o/vpd.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/board/w7o/vpd.c b/board/w7o/vpd.c new file mode 100644 index 0000000000..c24b127d3c --- /dev/null +++ b/board/w7o/vpd.c @@ -0,0 +1,408 @@ +/* + * (C) Copyright 2001 + * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#if defined(VXWORKS) +# include <stdio.h> +# include <string.h> +# define CFG_DEF_EEPROM_ADDR 0xa0 +extern char iicReadByte( char, char ); +extern ulong_t crc32( unsigned char *, unsigned long ); +#else +#include <common.h> +#endif + +#include "vpd.h" + +/* + * vpd_reader() - reads VPD data from I2C EEPROMS. + * returns pointer to buffer or NULL. + */ +static unsigned char * +vpd_reader(unsigned char *buf, unsigned dev_addr, unsigned off, unsigned count) +{ + unsigned offset = off; /* Calculated offset */ + + /* + * The main board EEPROM contains + * SDRAM SPD in the first 128 bytes, + * so skew the offset. + */ + if (dev_addr == CFG_DEF_EEPROM_ADDR) + offset += SDRAM_SPD_DATA_SIZE; + + /* Try to read the I2C EEPROM */ +#if defined(VXWORKS) + { + int i; + for( i = 0; i < count; ++i ) { + buf[ i ] = iicReadByte( dev_addr, offset+i ); + } + } +#else + if (eeprom_read(dev_addr, offset, buf, count)) { + printf("Failed to read %d bytes from VPD EEPROM 0x%x @ 0x%x\n", + count, dev_addr, offset); + return NULL; + } +#endif + + return buf; +} /* vpd_reader() */ + + +/* + * vpd_get_packet() - returns next VPD packet or NULL. + */ +static vpd_packet_t *vpd_get_packet(vpd_packet_t *vpd_packet) +{ + vpd_packet_t *packet = vpd_packet; + + if (packet != NULL) { + if (packet->identifier == VPD_PID_TERM) + return NULL; + else + packet = (vpd_packet_t *)((char *)packet + packet->size + 2); + } + + return packet; +} /* vpd_get_packet() */ + + +/* + * vpd_find_packet() - Locates and returns the specified + * VPD packet or NULL on error. + */ +static vpd_packet_t *vpd_find_packet(vpd_t *vpd, unsigned char ident) +{ + vpd_packet_t *packet = (vpd_packet_t *)&vpd->packets; + + /* Guaranteed illegal */ + if (ident == VPD_PID_GI) + return NULL; + + /* Scan tuples looking for a match */ + while ((packet->identifier != ident) && + (packet->identifier != VPD_PID_TERM)) + packet = vpd_get_packet(packet); + + /* Did we find it? */ + if ((packet->identifier) && (packet->identifier != ident)) + return NULL; + return packet; +} + + +/* + * vpd_is_valid() - Validates contents of VPD data + * in I2C EEPROM. Returns 1 for + * success or 0 for failure. + */ +static int vpd_is_valid(unsigned dev_addr, unsigned char *buf) +{ + unsigned num_bytes; + vpd_packet_t *packet; + vpd_t *vpd = (vpd_t *)buf; + unsigned short stored_crc16, calc_crc16 = 0xffff; + + /* Check Eyecatcher */ + if (strncmp(vpd->header.eyecatcher, VPD_EYECATCHER, VPD_EYE_SIZE) != 0) { + unsigned offset = 0; + if (dev_addr == CFG_DEF_EEPROM_ADDR) + offset += SDRAM_SPD_DATA_SIZE; + printf("Error: VPD EEPROM 0x%x corrupt @ 0x%x\n", dev_addr, offset); + + return 0; + } + + /* Check Length */ + if (vpd->header.size> VPD_MAX_EEPROM_SIZE) { + printf("Error: VPD EEPROM 0x%x contains bad size 0x%x\n", + dev_addr, vpd->header.size); + return 0; + } + + /* Now find the termination packet */ + if ((packet = vpd_find_packet(vpd, VPD_PID_TERM)) == NULL) { + printf("Error: VPD EEPROM 0x%x missing termination packet\n", + dev_addr); + return 0; + } + + /* Calculate data size */ + num_bytes = (unsigned long)((unsigned char *)packet - + (unsigned char *)vpd + sizeof(vpd_packet_t)); + + /* Find stored CRC and clear it */ + if ((packet = vpd_find_packet(vpd, VPD_PID_CRC)) == NULL) { + printf("Error: VPD EEPROM 0x%x missing CRC\n", dev_addr); + return 0; + } + stored_crc16 = *((ushort *)packet->data); + *(ushort *)packet->data = 0; + + /* OK, lets calculate the CRC and check it */ +#if defined(VXWORKS) + calc_crc16 = (0xffff & crc32(buf, num_bytes)); +#else + calc_crc16 = (0xffff & crc32(0, buf, num_bytes)); +#endif + *(ushort *)packet->data = stored_crc16; /* Now restore the CRC */ + if (stored_crc16 != calc_crc16) { + printf("Error: VPD EEPROM 0x%x has bad CRC 0x%x\n", + dev_addr, stored_crc16); + return 0; + } + + return 1; +} /* vpd_is_valid() */ + + +/* + * size_ok() - Check to see if packet size matches + * size of data we want. Returns 1 for + * good match or 0 for failure. + */ +static int size_ok(vpd_packet_t *packet, unsigned long size) +{ + if (packet->size != size) { + printf("VPD Packet 0x%x corrupt.\n", packet->identifier); + return 0; + } + return 1; +} /* size_ok() */ + + +/* + * strlen_ok() - Check to see if packet size matches + * strlen of the string we want to populate. + * Returns 1 for valid length or 0 for failure. + */ +static int strlen_ok(vpd_packet_t *packet, unsigned long length) +{ + if (packet->size >= length) { + printf("VPD Packet 0x%x corrupt.\n", packet->identifier); + return 0; + } + return 1; +} /* strlen_ok() */ + + +/* + * get_vpd_data() - populates the passed VPD structure 'vpdInfo' + * with data obtained from the specified + * I2C EEPROM 'dev_addr'. Returns 0 for + * success or 1 for failure. + */ +int vpd_get_data(unsigned char dev_addr, VPD *vpdInfo) +{ + unsigned char buf[VPD_EEPROM_SIZE]; + vpd_t *vpd = (vpd_t *)buf; + vpd_packet_t *packet; + + if (vpdInfo == NULL) + return 1; + + /* + * Fill vpdInfo with 0s to blank out + * unused fields, fill vpdInfo->ethAddrs + * with all 0xffs so that other's code can + * determine how many real Ethernet addresses + * there are. OUIs starting with 0xff are + * broadcast addresses, and would never be + * permantely stored. + */ + memset((void *)vpdInfo, 0, sizeof(VPD)); + memset((void *)&vpdInfo->ethAddrs, 0xff, sizeof(vpdInfo->ethAddrs)); + vpdInfo->_devAddr = dev_addr; + + /* Read the minimum size first */ + if (vpd_reader(buf, dev_addr, 0, VPD_EEPROM_SIZE) == NULL) { + return 1; + } + + /* Check validity of VPD data */ + if (!vpd_is_valid(dev_addr, buf)) { + printf("VPD Data is INVALID!\n"); + return 1; + } + + /* + * Walk all the packets and populate + * the VPD info structure. + */ + packet = (vpd_packet_t *)&vpd->packets; + do { + switch (packet->identifier) { + case VPD_PID_GI: + printf("Error: Illegal VPD value\n"); + break; + case VPD_PID_PID: + if (strlen_ok(packet, MAX_PROD_ID)) { + strncpy(vpdInfo->productId, + packet->data, packet->size); + } + break; + case VPD_PID_REV: + if (size_ok(packet, sizeof(char))) + vpdInfo->revisionId = *packet->data; + break; + case VPD_PID_SN: + if (size_ok(packet, sizeof(unsigned long))) { + vpdInfo->serialNum = + *(unsigned long *)packet->data; + } + break; + case VPD_PID_MANID: + if (size_ok(packet, sizeof(unsigned char))) + vpdInfo->manuID = *packet->data; + break; + case VPD_PID_PCO: + if (size_ok(packet, sizeof(unsigned long))) { + vpdInfo->configOpt = + *(unsigned long *)packet->data; + } + break; + case VPD_PID_SYSCLK: + if (size_ok(packet, sizeof(unsigned long))) + vpdInfo->sysClk = *(unsigned long *)packet->data; + break; + case VPD_PID_SERCLK: + if (size_ok(packet, sizeof(unsigned long))) + vpdInfo->serClk = *(unsigned long *)packet->data; + break; + case VPD_PID_FLASH: + if (size_ok(packet, 9)) { /* XXX - hardcoded, + padding in struct */ + memcpy(&vpdInfo->flashCfg, packet->data, 9); + } + break; + case VPD_PID_ETHADDR: + memcpy(vpdInfo->ethAddrs, packet->data, packet->size); + break; + case VPD_PID_POTS: + if (size_ok(packet, sizeof(char))) + vpdInfo->numPOTS = (unsigned)*packet->data; + break; + case VPD_PID_DS1: + if (size_ok(packet, sizeof(char))) + vpdInfo->numDS1 = (unsigned)*packet->data; + case VPD_PID_GAL: + case VPD_PID_CRC: + case VPD_PID_TERM: + break; + default: + printf("Warning: Found unknown VPD packet ID 0x%x\n", + packet->identifier); + break; + } + } while ((packet = vpd_get_packet(packet))); + + return 0; +} /* end get_vpd_data() */ + + +/* + * vpd_init() - Initialize default VPD environment + */ +int vpd_init(unsigned char dev_addr) +{ + return (0); +} /* vpd_init() */ + + +/* + * vpd_print() - Pretty print the VPD data. + */ +void vpd_print(VPD *vpdInfo) +{ + const char *const sp = ""; + const char *const sfmt = "%4s%-20s: \"%s\"\n"; + const char *const cfmt = "%4s%-20s: '%c'\n"; + const char *const dfmt = "%4s%-20s: %ld\n"; + const char *const hfmt = "%4s%-20s: %08lX\n"; + const char *const dsfmt = "%4s%-20s: %d\n"; + const char *const hsfmt = "%4s%-20s: %04X\n"; + const char *const dhfmt = "%4s%-20s: %ld (%lX)\n"; + + printf("VPD read from I2C device: %02X\n", vpdInfo->_devAddr); + + if (vpdInfo->productId[0]) + printf(sfmt, sp, "Product ID", vpdInfo->productId); + else + printf(sfmt, sp, "Product ID", "UNKNOWN"); + + if (vpdInfo->revisionId) + printf(cfmt, sp, "Revision ID", vpdInfo->revisionId); + + if (vpdInfo->serialNum) + printf(dfmt, sp, "Serial Number", vpdInfo->serialNum); + + if (vpdInfo->manuID) + printf(dfmt, sp, "Manufacture ID", (long)vpdInfo->manuID); + + if (vpdInfo->configOpt) + printf(hfmt, sp, "Configuration", vpdInfo->configOpt); + + if (vpdInfo->sysClk) + printf(dhfmt, sp, "System Clock", vpdInfo->sysClk, vpdInfo->sysClk); + + if (vpdInfo->serClk) + printf(dhfmt, sp, "Serial Clock", vpdInfo->serClk, vpdInfo->serClk); + + if (vpdInfo->numPOTS) + printf(dfmt, sp, "Number of POTS lines", vpdInfo->numPOTS); + + if (vpdInfo->numDS1) + printf(dfmt, sp, "Number of DS1s", vpdInfo->numDS1); + + /* Print Ethernet Addresses */ + if (vpdInfo->ethAddrs[0][0] != 0xff) { + int i, j; + printf("%4sEtherNet Address(es): ", sp); + for (i = 0; i < MAX_ETH_ADDRS; i++) { + if (vpdInfo->ethAddrs[i][0] != 0xff) { + for (j = 0; j < 6; j++) { + printf("%02X", vpdInfo->ethAddrs[i][j]); + if (((j + 1) % 6) != 0) + printf(":"); + else + printf(" "); + } + if (((i + 1) % 3) == 0) printf("\n%24s: ", sp); + } + } + printf("\n"); + } + + if (vpdInfo->flashCfg.mfg && vpdInfo->flashCfg.dev) { + printf("Main Flash Configuration:\n"); + printf(hsfmt, sp, "Manufacture ID", vpdInfo->flashCfg.mfg); + printf(hsfmt, sp, "Device ID", vpdInfo->flashCfg.dev); + printf(dsfmt, sp, "Device Width", vpdInfo->flashCfg.devWidth); + printf(dsfmt, sp, "Num. Devices", vpdInfo->flashCfg.numDevs); + printf(dsfmt, sp, "Num. Columns", vpdInfo->flashCfg.numCols); + printf(dsfmt, sp, "Column Width", vpdInfo->flashCfg.colWidth); + printf(dsfmt, sp, "WE Data Width", vpdInfo->flashCfg.weDataWidth); + } +} /* vpd_print() */ + |