uboot/examples/standalone/82559_eeprom.c
<<
>>
Prefs
   1
   2/*
   3 * Copyright 1998-2001 by Donald Becker.
   4 * This software may be used and distributed according to the terms of
   5 * the GNU General Public License (GPL), incorporated herein by reference.
   6 * Contact the author for use under other terms.
   7 *
   8 * This program must be compiled with "-O"!
   9 * See the bottom of this file for the suggested compile-command.
  10 *
  11 * The author may be reached as becker@scyld.com, or C/O
  12 *  Scyld Computing Corporation
  13 *  410 Severn Ave., Suite 210
  14 *  Annapolis MD 21403
  15 *
  16 * Common-sense licensing statement: Using any portion of this program in
  17 * your own program means that you must give credit to the original author
  18 * and release the resulting code under the GPL.
  19 */
  20
  21#define _PPC_STRING_H_          /* avoid unnecessary str/mem functions */
  22
  23#include <common.h>
  24#include <exports.h>
  25#include <asm/io.h>
  26
  27
  28/* Default EEPROM for i82559 */
  29static unsigned short default_eeprom[64] = {
  30        0x0100, 0x0302, 0x0504, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  31        0xffff, 0xffff, 0x40c0, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff,
  32        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  33        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  34        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  35        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  36        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  37        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
  38};
  39
  40static unsigned short eeprom[256];
  41
  42static int eeprom_size = 64;
  43static int eeprom_addr_size = 6;
  44
  45static int debug = 0;
  46
  47static inline unsigned short swap16(unsigned short x)
  48{
  49        return (((x & 0xff) << 8) | ((x & 0xff00) >> 8));
  50}
  51
  52
  53void * memcpy(void * dest,const void *src,size_t count)
  54{
  55        char *tmp = (char *) dest, *s = (char *) src;
  56
  57        while (count--)
  58                *tmp++ = *s++;
  59
  60        return dest;
  61}
  62
  63
  64/* The EEPROM commands include the alway-set leading bit. */
  65#define EE_WRITE_CMD    (5)
  66#define EE_READ_CMD             (6)
  67#define EE_ERASE_CMD    (7)
  68
  69/* Serial EEPROM section. */
  70#define EE_SHIFT_CLK    0x01    /* EEPROM shift clock. */
  71#define EE_CS                   0x02    /* EEPROM chip select. */
  72#define EE_DATA_WRITE   0x04    /* EEPROM chip data in. */
  73#define EE_DATA_READ    0x08    /* EEPROM chip data out. */
  74#define EE_ENB                  (0x4800 | EE_CS)
  75#define EE_WRITE_0              0x4802
  76#define EE_WRITE_1              0x4806
  77#define EE_OFFSET               14
  78
  79/* Delay between EEPROM clock transitions. */
  80#define eeprom_delay(ee_addr)   inw(ee_addr)
  81
  82/* Wait for the EEPROM to finish the previous operation. */
  83static int eeprom_busy_poll(long ee_ioaddr)
  84{
  85        int i;
  86        outw(EE_ENB, ee_ioaddr);
  87        for (i = 0; i < 10000; i++)                     /* Typical 2000 ticks */
  88                if (inw(ee_ioaddr) & EE_DATA_READ)
  89                        break;
  90        return i;
  91}
  92
  93/* This executes a generic EEPROM command, typically a write or write enable.
  94   It returns the data output from the EEPROM, and thus may also be used for
  95   reads. */
  96static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
  97{
  98        unsigned retval = 0;
  99        long ee_addr = ioaddr + EE_OFFSET;
 100
 101        if (debug > 1)
 102                printf(" EEPROM op 0x%x: ", cmd);
 103
 104        outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
 105
 106        /* Shift the command bits out. */
 107        do {
 108                short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
 109                outw(dataval, ee_addr);
 110                eeprom_delay(ee_addr);
 111                if (debug > 2)
 112                        printf("%X", inw(ee_addr) & 15);
 113                outw(dataval | EE_SHIFT_CLK, ee_addr);
 114                eeprom_delay(ee_addr);
 115                retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
 116        } while (--cmd_len >= 0);
 117#if 0
 118        outw(EE_ENB, ee_addr);
 119#endif
 120        /* Terminate the EEPROM access. */
 121        outw(EE_ENB & ~EE_CS, ee_addr);
 122        if (debug > 1)
 123                printf(" EEPROM result is 0x%5.5x.\n", retval);
 124        return retval;
 125}
 126
 127static int read_eeprom(long ioaddr, int location, int addr_len)
 128{
 129        return do_eeprom_cmd(ioaddr, ((EE_READ_CMD << addr_len) | location)
 130                << 16 , 3 + addr_len + 16) & 0xffff;
 131}
 132
 133static void write_eeprom(long ioaddr, int index, int value, int addr_len)
 134{
 135        long ee_ioaddr = ioaddr + EE_OFFSET;
 136        int i;
 137
 138        /* Poll for previous op finished. */
 139        eeprom_busy_poll(ee_ioaddr);                    /* Typical 0 ticks */
 140        /* Enable programming modes. */
 141        do_eeprom_cmd(ioaddr, (0x4f << (addr_len-4)), 3 + addr_len);
 142        /* Do the actual write. */
 143        do_eeprom_cmd(ioaddr,
 144                                  (((EE_WRITE_CMD<<addr_len) | index)<<16) | (value & 0xffff),
 145                                  3 + addr_len + 16);
 146        /* Poll for write finished. */
 147        i = eeprom_busy_poll(ee_ioaddr);                        /* Typical 2000 ticks */
 148        if (debug)
 149                printf(" Write finished after %d ticks.\n", i);
 150        /* Disable programming. This command is not instantaneous, so we check
 151           for busy before the next op. */
 152        do_eeprom_cmd(ioaddr, (0x40 << (addr_len-4)), 3 + addr_len);
 153        eeprom_busy_poll(ee_ioaddr);
 154}
 155
 156static int reset_eeprom(unsigned long ioaddr, unsigned char *hwaddr)
 157{
 158        unsigned short checksum = 0;
 159        int size_test;
 160        int i;
 161
 162        printf("Resetting i82559 EEPROM @ 0x%08lx ... ", ioaddr);
 163
 164        size_test = do_eeprom_cmd(ioaddr, (EE_READ_CMD << 8) << 16, 27);
 165        eeprom_addr_size = (size_test & 0xffe0000) == 0xffe0000 ? 8 : 6;
 166        eeprom_size = 1 << eeprom_addr_size;
 167
 168        memcpy(eeprom, default_eeprom, sizeof default_eeprom);
 169
 170        for (i = 0; i < 3; i++)
 171                eeprom[i] = (hwaddr[i*2+1]<<8) + hwaddr[i*2];
 172
 173        /* Recalculate the checksum. */
 174        for (i = 0; i < eeprom_size - 1; i++)
 175                checksum += eeprom[i];
 176        eeprom[i] = 0xBABA - checksum;
 177
 178        for (i = 0; i < eeprom_size; i++)
 179                write_eeprom(ioaddr, i, eeprom[i], eeprom_addr_size);
 180
 181        for (i = 0; i < eeprom_size; i++)
 182                if (read_eeprom(ioaddr, i, eeprom_addr_size) != eeprom[i]) {
 183                        printf("failed\n");
 184                        return 1;
 185                }
 186
 187        printf("done\n");
 188        return 0;
 189}
 190
 191static unsigned int hatoi(char *p, char **errp)
 192{
 193        unsigned int res = 0;
 194
 195        while (1) {
 196                switch (*p) {
 197                case 'a':
 198                case 'b':
 199                case 'c':
 200                case 'd':
 201                case 'e':
 202                case 'f':
 203                        res |= (*p - 'a' + 10);
 204                        break;
 205                case 'A':
 206                case 'B':
 207                case 'C':
 208                case 'D':
 209                case 'E':
 210                case 'F':
 211                        res |= (*p - 'A' + 10);
 212                        break;
 213                case '0':
 214                case '1':
 215                case '2':
 216                case '3':
 217                case '4':
 218                case '5':
 219                case '6':
 220                case '7':
 221                case '8':
 222                case '9':
 223                        res |= (*p - '0');
 224                        break;
 225                default:
 226                        if (errp) {
 227                                *errp = p;
 228                        }
 229                return res;
 230                }
 231                p++;
 232                if (*p == 0) {
 233                        break;
 234                }
 235                res <<= 4;
 236        }
 237
 238        if (errp) {
 239                *errp = NULL;
 240        }
 241
 242        return res;
 243}
 244
 245static unsigned char *gethwaddr(char *in, unsigned char *out)
 246{
 247        char tmp[3];
 248        int i;
 249        char *err;
 250
 251        for (i=0;i<6;i++) {
 252                if (in[i*3+2] == 0 && i == 5) {
 253                        out[i] = hatoi(&in[i*3], &err);
 254                        if (err) {
 255                                return NULL;
 256                        }
 257                } else if (in[i*3+2] == ':' && i < 5) {
 258                        tmp[0] = in[i*3];
 259                        tmp[1] = in[i*3+1];
 260                        tmp[2] = 0;
 261                        out[i] = hatoi(tmp, &err);
 262                        if (err) {
 263                                return NULL;
 264                        }
 265                } else {
 266                        return NULL;
 267                }
 268        }
 269
 270        return out;
 271}
 272
 273static u32
 274read_config_dword(int bus, int dev, int func, int reg)
 275{
 276        u32 res;
 277
 278        outl(0x80000000|(bus&0xff)<<16|(dev&0x1f)<<11|(func&7)<<8|(reg&0xfc),
 279             0xcf8);
 280        res = inl(0xcfc);
 281        outl(0, 0xcf8);
 282        return res;
 283}
 284
 285static u16
 286read_config_word(int bus, int dev, int func, int reg)
 287{
 288        u32 res;
 289
 290        outl(0x80000000|(bus&0xff)<<16|(dev&0x1f)<<11|(func&7)<<8|(reg&0xfc),
 291             0xcf8);
 292        res = inw(0xcfc + (reg & 2));
 293        outl(0, 0xcf8);
 294        return res;
 295}
 296
 297static void
 298write_config_word(int bus, int dev, int func, int reg, u16 data)
 299{
 300
 301        outl(0x80000000|(bus&0xff)<<16|(dev&0x1f)<<11|(func&7)<<8|(reg&0xfc),
 302             0xcf8);
 303        outw(data, 0xcfc + (reg & 2));
 304        outl(0, 0xcf8);
 305}
 306
 307
 308int main (int argc, char * const argv[])
 309{
 310        unsigned char *eth_addr;
 311        uchar buf[6];
 312        int instance;
 313
 314        app_startup(argv);
 315        if (argc != 2) {
 316                printf ("call with base Ethernet address\n");
 317                return 1;
 318        }
 319
 320
 321        eth_addr = gethwaddr(argv[1], buf);
 322        if (NULL == eth_addr) {
 323                printf ("Can not parse ethernet address\n");
 324                return 1;
 325        }
 326        if (eth_addr[5] & 0x01) {
 327                printf("Base Ethernet address must be even\n");
 328        }
 329
 330
 331        for (instance = 0; instance < 2; instance ++)  {
 332                unsigned int io_addr;
 333                unsigned char mac[6];
 334                int bar1 = read_config_dword(0, 6+instance, 0, 0x14);
 335                if (! (bar1 & 1)) {
 336                        printf("ETH%d is disabled %x\n", instance, bar1);
 337                } else {
 338                        printf("ETH%d IO=0x%04x\n", instance, bar1 & ~3);
 339                }
 340                io_addr = (bar1 & (~3L));
 341
 342
 343                write_config_word(0, 6+instance, 0, 4,
 344                                  read_config_word(0, 6+instance, 0, 4) | 1);
 345                printf("ETH%d CMD %04x\n", instance,
 346                           read_config_word(0, 6+instance, 0, 4));
 347
 348                memcpy(mac, eth_addr, 6);
 349                mac[5] += instance;
 350
 351                printf("got io=%04x, ha=%02x:%02x:%02x:%02x:%02x:%02x\n",
 352                           io_addr, mac[0], mac[1], mac[2],
 353                           mac[3], mac[4], mac[5]);
 354                reset_eeprom(io_addr, mac);
 355        }
 356        return 0;
 357}
 358