/****************************************************************************** * * Author: Xilinx, Inc. * * * 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. * * * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A * COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS * ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, * XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE * FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING * ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY * WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM * CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. * * * Xilinx hardware products are not intended for use in life support * appliances, devices, or systems. Use in such applications is * expressly prohibited. * * * (c) Copyright 2002-2004 Xilinx Inc. * All rights reserved. * * * 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., * 675 Mass Ave, Cambridge, MA 02139, USA. * ******************************************************************************/ #include #include #include #include #include "xparameters.h" #ifdef CFG_ENV_IS_IN_EEPROM #include #include "xiic_l.h" #define IIC_DELAY 5000 static u8 envStep = 0; /* 0 means crc has not been read */ const u8 hex[] = "0123456789ABCDEF"; /* lookup table for ML300 CRC */ /************************************************************************ * Use Xilinx provided driver to send data to EEPROM using iic bus. */ static void send(u32 adr, u8 * data, u32 len) { u8 sendBuf[34]; /* first 2-bit is address and others are data */ u32 pos, wlen; u32 ret; wlen = 32; for (pos = 0; pos < len; pos += 32) { if ((len - pos) < 32) wlen = len - pos; /* Put address and data bits together */ sendBuf[0] = (u8) ((adr + pos) >> 8); sendBuf[1] = (u8) (adr + pos); memcpy(&sendBuf[2], &data[pos], wlen); /* Send to EEPROM through iic bus */ ret = XIic_Send(XPAR_IIC_0_BASEADDR, CFG_I2C_EEPROM_ADDR >> 1, sendBuf, wlen + 2); udelay(IIC_DELAY); } } /************************************************************************ * Use Xilinx provided driver to read data from EEPROM using the iic bus. */ static void receive(u32 adr, u8 * data, u32 len) { u8 address[2]; u32 ret; address[0] = (u8) (adr >> 8); address[1] = (u8) adr; /* Provide EEPROM address */ ret = XIic_Send(XPAR_IIC_0_BASEADDR, CFG_I2C_EEPROM_ADDR >> 1, address, 2); /* Receive data from EEPROM */ ret = XIic_Recv(XPAR_IIC_0_BASEADDR, CFG_I2C_EEPROM_ADDR >> 1, data, len); } /************************************************************************ * Convert a hexadecimal string to its equivalent integer value. */ static u8 axtoi(u8 * hexStg) { u8 n; /* position in string */ u8 m; /* position in digit[] to shift */ u8 count; /* loop index */ u8 intValue; /* integer value of hex string */ u8 digit[2]; /* hold values to convert */ for (n = 0; n < 2; n++) { if (hexStg[n] == '\0') break; if (hexStg[n] > 0x29 && hexStg[n] < 0x40) digit[n] = hexStg[n] & 0x0f; else if (hexStg[n] >= 'a' && hexStg[n] <= 'f') digit[n] = (hexStg[n] & 0x0f) + 9; else if (hexStg[n] >= 'A' && hexStg[n] <= 'F') digit[n] = (hexStg[n] & 0x0f) + 9; else break; } intValue = 0; count = n; m = n - 1; n = 0; while (n < count) { intValue = intValue | (digit[n] << (m << 2)); m--; /* adjust the position to set */ n++; /* next digit to process */ } return (intValue); } /************************************************************************ * Convert an integer string to its equivalent value. */ static u8 atoi(uchar * string) { u8 res = 0; while (*string >= '0' && *string <= '9') { res *= 10; res += *string - '0'; string++; } return res; } /************************************************************************ * Key-value pairs are separated by "=" sign. */ static void findKey(uchar * buffer, int *loc, u8 len) { u32 i; for (i = 0; i < len; i++) if (buffer[i] == '=') { *loc = i; return; } /* return -1 is no "=" sign found */ *loc = -1; } /************************************************************************ * Compute a new ML300 CRC when user calls the saveenv command. * Also update EEPROM with new CRC value. */ static u8 update_crc(u32 len, uchar * data) { uchar temp[6] = { 'C', '=', 0x00, 0x00, 0x00, 0x00 }; u32 crc; /* new crc value */ u32 i; crc = 0; /* calculate new CRC */ for (i = 0; i < len; i++) crc += data[i]; /* CRC includes key for check sum */ crc += 'C' + '='; /* compose new CRC to be updated */ temp[2] = hex[(crc >> 4) & 0xf]; temp[3] = hex[crc & 0xf]; /* check to see if env size exceeded */ if (len + 6 > ENV_SIZE) { printf("ERROR: not enough space to store CRC on EEPROM"); return 1; } memcpy(data + len, temp, 6); return 0; } /************************************************************************ * Read out ML300 CRC and compare it with a runtime calculated ML300 CRC. * If equal, then pass back a u-boot CRC value, otherwise pass back * junk to indicate CRC error. */ static void read_crc(uchar * buffer, int len) { u32 addr, n; u32 crc; /* runtime crc */ u8 old[2] = { 0xff, 0xff }; /* current CRC in EEPROM */ u8 stop; /* indication of end of env data */ u8 pre; /* previous EEPROM data bit */ int i, loc; addr = CFG_ENV_OFFSET; /* start from first env address */ n = 0; pre = 1; stop = 1; crc = 0; /* calculate runtime CRC according to ML300 and read back old CRC stored in the EEPROM */ while (n < CFG_ENV_SIZE) { receive(addr, buffer, len); /* found two null chars, end of env */ if ((pre || buffer[0]) == 0) break; findKey(buffer, &loc, len); /* found old check sum, read and store old CRC */ if ((loc == 0 && pre == 'C') || (loc > 0 && buffer[loc - 1] == 'C')) receive(addr + loc + 1, old, 2); pre = buffer[len - 1]; /* calculate runtime ML300 CRC */ crc += buffer[0]; i = 1; do { crc += buffer[i]; stop = buffer[i] || buffer[i - 1]; i++; } while (stop && (i < len)); if (stop == 0) break; n += len; addr += len; } /* exclude old CRC from runtime calculation */ crc -= (old[0] + old[1]); /* match CRC values, send back u-boot CRC */ if ((old[0] == hex[(crc >> 4) & 0xf]) && (old[1] == hex[crc & 0xf])) { crc = 0; n = 0; addr = CFG_ENV_OFFSET - offsetof(env_t, crc) + offsetof(env_t, data); /* calculate u-boot crc */ while (n < ENV_SIZE) { receive(addr, buffer, len); crc = crc32(crc, buffer, len); n += len; addr += len; } memcpy(buffer, &crc, 4); } } /************************************************************************ * Convert IP address to hexadecimals. */ static void ip_ml300(uchar * s, uchar * res) { char temp[2]; u8 i; res[0] = 0x00; for (i = 0; i < 4; i++) { sprintf(temp, "%02x", atoi(s)); s = (uchar *)strchr((char *)s, '.') + 1; strcat((char *)res, temp); } } /************************************************************************ * Change 0xff (255), a dummy null char to 0x00. */ static void change_null(uchar * s) { if (s != NULL) { change_null((uchar *)strchr((char *)s + 1, 255)); *(strchr((char *)s, 255)) = '\0'; } } /************************************************************************ * Update environment variable name and values to u-boot standard. */ void convert_env(void) { char *s; /* pointer to env value */ char temp[20]; /* temp storage for addresses */ /* E -> ethaddr */ s = getenv("E"); if (s != NULL) { sprintf(temp, "%c%c.%c%c.%c%c.%c%c.%c%c.%c%c", s[0], s[1], s[ 2], s[ 3], s[4], s[5], s[ 6], s[ 7], s[8], s[9], s[10], s[11] ); setenv("ethaddr", temp); setenv("E", NULL); } /* L -> serial# */ s = getenv("L"); if (s != NULL) { setenv("serial#", s); setenv("L", NULL); } /* I -> ipaddr */ s = getenv("I"); if (s != NULL) { sprintf(temp, "%d.%d.%d.%d", axtoi((u8 *)s), axtoi((u8 *)(s + 2)), axtoi((u8 *)(s + 4)), axtoi((u8 *)(s + 6))); setenv("ipaddr", temp); setenv("I", NULL); } /* S -> serverip */ s = getenv("S"); if (s != NULL) { sprintf(temp, "%d.%d.%d.%d", axtoi((u8 *)s), axtoi((u8 *)(s + 2)), axtoi((u8 *)(s + 4)), axtoi((u8 *)(s + 6))); setenv("serverip", temp); setenv("S", NULL); } /* A -> bootargs */ s = getenv("A"); if (s != NULL) { setenv("bootargs", s); setenv("A", NULL); } /* F -> bootfile */ s = getenv("F"); if (s != NULL) { setenv("bootfile", s); setenv("F", NULL); } /* M -> bootcmd */ s = getenv("M"); if (s != NULL) { setenv("bootcmd", s); setenv("M", NULL); } /* Don't include C (CRC) */ setenv("C", NULL); } /************************************************************************ * Save user modified environment values back to EEPROM. */ static void save_env(void) { char eprom[ENV_SIZE]; /* buffer to be written back to EEPROM */ char *s, temp[20]; char ff[] = { 0xff, 0x00 }; /* dummy null value */ u32 len; /* length of env to be written to EEPROM */ eprom[0] = 0x00; /* ethaddr -> E */ s = getenv("ethaddr"); if (s != NULL) { strcat(eprom, "E="); sprintf(temp, "%c%c%c%c%c%c%c%c%c%c%c%c", *s, *(s + 1), *(s + 3), *(s + 4), *(s + 6), *(s + 7), *(s + 9), *(s + 10), *(s + 12), *(s + 13), *(s + 15), *(s + 16)); strcat(eprom, temp); strcat(eprom, ff); } /* serial# -> L */ s = getenv("serial#"); if (s != NULL) { strcat(eprom, "L="); strcat(eprom, s); strcat(eprom, ff); } /* ipaddr -> I */ s = getenv("ipaddr"); if (s != NULL) { strcat(eprom, "I="); ip_ml300((uchar *)s, (uchar *)temp); strcat(eprom, temp); strcat(eprom, ff); } /* serverip -> S */ s = getenv("serverip"); if (s != NULL) { strcat(eprom, "S="); ip_ml300((uchar *)s, (uchar *)temp); strcat(eprom, temp); strcat(eprom, ff); } /* bootargs -> A */ s = getenv("bootargs"); if (s != NULL) { strcat(eprom, "A="); strcat(eprom, s); strcat(eprom, ff); } /* bootfile -> F */ s = getenv("bootfile"); if (s != NULL) { strcat(eprom, "F="); strcat(eprom, s); strcat(eprom, ff); } /* bootcmd -> M */ s = getenv("bootcmd"); if (s != NULL) { strcat(eprom, "M="); strcat(eprom, s); strcat(eprom, ff); } len = strlen(eprom); /* find env length without crc */ change_null((uchar *)eprom); /* change 0xff to 0x00 */ /* update EEPROM env values if there is enough space */ if (update_crc(len, (uchar *)eprom) == 0) send(CFG_ENV_OFFSET, (uchar *)eprom, len + 6); } /************************************************************************ * U-boot call for EEPROM read associated activities. */ int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len) { if (envStep == 0) { /* first read call is for crc */ read_crc(buffer, len); ++envStep; return 0; } else if (envStep == 1) { /* then read out EEPROM content for runtime u-boot CRC calculation */ receive(addr, buffer, len); if (addr + len - CFG_ENV_OFFSET == CFG_ENV_SIZE) /* end of runtime crc read */ ++envStep; return 0; } if (len < 2) { /* when call getenv_r */ receive(addr, buffer, len); } else if (addr + len < CFG_ENV_OFFSET + CFG_ENV_SIZE) { /* calling env_relocate(), but don't read out crc value from EEPROM */ receive(addr, buffer + 4, len); } else { receive(addr, buffer + 4, len - 4); } return 0; } /************************************************************************ * U-boot call for EEPROM write acativities. */ int i2c_write(uchar chip, uint addr, int alen, uchar * buffer, int len) { /* save env on last page write called by u-boot */ if (addr + len >= CFG_ENV_OFFSET + CFG_ENV_SIZE) save_env(); return 0; } /************************************************************************ * Dummy function. */ int i2c_probe(uchar chip) { return 1; } #endif