home *** CD-ROM | disk | FTP | other *** search
/ linuxmafia.com 2016 / linuxmafia.com.tar / linuxmafia.com / pub / hardware / el3.c < prev    next >
C/C++ Source or Header  |  2003-03-26  |  11KB  |  397 lines

  1. /* el3-diag.c: Diagnostic program for 3c509 and 3c579 ethercards.
  2.  
  3.     A program to test the ISA activation mechanism of the 3c509 and 3c509b
  4.     ISA Ethernet adapters.
  5.  
  6.     The instructions are at
  7.     http://www.scyld.com/diag/index.html
  8.  
  9.     Written 1993-2000 by Donald Becker.
  10.     Copyright 2000 by Scyld Computing Corporation
  11.     Copyright 1994-1999 Donald Becker
  12.     Copyright 1993 United States Government as represented by the
  13.     Director, National Security Agency.
  14.  
  15.     This software may be used and distributed according to the terms of
  16.     the GNU General Public License (GPL), incorporated herein by reference.
  17.  
  18.     This program must be compiled with "-O"!
  19.     See the bottom of this file for the suggested compile-command.
  20.  
  21.     The author may be reached as becker@scyld.com, or C/O
  22.      Scyld Computing Corporation
  23.      410 Severn Ave., Suite 210
  24.      Annapolis MD 21403
  25.  
  26.     Support and updates available at
  27.      http://www.scyld.com/diag/index.html
  28.  
  29.     References
  30.      The 3Com EtherLink III manual, available from 3Com
  31. */
  32.  
  33. static char *version_msg =
  34. "el3diag.c:v1.01 6/19/2000 Donald Becker (becker@scyld.com)\n"
  35. " http://www.scyld.com/diag/index.html\n";
  36.  
  37. #if ! defined(__OPTIMIZE__)
  38. #warning  You must compile this program with the correct options!
  39. #warning  See the last lines of the source file.
  40. #error You must compile this driver with "-O".
  41. #endif
  42. #include <unistd.h>
  43. #include <stdio.h>
  44. #include <stdlib.h>
  45. #include <getopt.h>
  46. #include <sys/time.h>
  47.  
  48. #if defined(__linux__)  &&  __GNU_LIBRARY__ == 1
  49. #include <asm/io.h>            /* Newer libraries use <sys/io.h> instead. */
  50. #else
  51. #include <sys/io.h>
  52. /* Use   extern iopl(int level);  if your glibc does not define it. */
  53. #endif
  54.  
  55. struct option longopts[] = {
  56.  /* { name    has_arg     *flag    val } */
  57.     {"base-address", 1, 0, 'p'},
  58.     {"all",           0, 0, 'a'},    /* Print all registers. */
  59.     {"help",       0, 0, 'h'},    /* Give help */
  60.     {"interface",  0, 0, 'f'},    /* Interface number (built-in, AUI) */
  61.     {"irq",           1, 0, 'i'},    /* Interrupt number */
  62.     {"quiet",       0, 0, 'q'},    /* Tone down verbosity mode */
  63.     {"verbose",       0, 0, 'v'},    /* Verbose mode */
  64.     {"version",       0, 0, 'V'},    /* Display version number */
  65.     {"write-EEPROM", 1, 0, 'w'},/* Write th EEPROMS with the specified vals */
  66.     { 0, 0, 0, 0 }
  67. };
  68.  
  69. /* These mappings allow the driver code to be used verbatim, so that
  70.    the behavior matches exactly. */
  71. #define printk printf
  72.  
  73. #ifdef EL3_DEBUG
  74. int el3_debug = EL3_DEBUG;
  75. #else
  76. int el3_debug = 6;
  77. #endif
  78.  
  79. /* Offsets from base I/O address. */
  80. #define EL3_DATA 0x00
  81. #define EL3_CMD 0x0e
  82. #define     EEPROM_READ 0x80
  83.  
  84. /* Register window 1 offsets, used in normal operation. */
  85. #define TX_FREE 0x0C
  86. #define TX_STATUS 0x0B
  87. #define TX_FIFO 0x00
  88. #define RX_FIFO 0x00
  89.  
  90. int el3_probe(void);
  91.  
  92. /* A minimal device structure so that we can use the driver transmit code
  93.    with as few changes as possible. */
  94. struct device {
  95.     char *name;
  96.     short base_addr;
  97.     int tbusy;
  98.     int trans_start;
  99. } devs, *dev;
  100.  
  101. int verbose = 1;
  102. int jiffies;
  103. unsigned char fake_packet[100];
  104.  
  105. int el3_send_packet(void *pkt, int len);
  106. static ushort id_read_eeprom(int id_port, int index);
  107.  
  108.  
  109.  
  110. int
  111. main(int argc, char **argv)
  112. {
  113.     int port_base = -1, irq = -1;
  114.     int errflag = 0, shared_mode = 0;
  115.     int write_eeprom = 0, interface = -1, all_regs = 0;
  116.     int c, card, longind;
  117.     extern char *optarg;
  118.     dev = &devs;
  119.  
  120.     while ((c = getopt_long(argc, argv, "af:i:p:qsvw", longopts, &longind))
  121.            != -1)
  122.         switch (c) {
  123.         case 'f':
  124.             interface = atoi(optarg);
  125.             break;
  126.         case 'i':
  127.             irq = atoi(optarg);
  128.             break;
  129.         case 'p':
  130.             port_base = strtol(optarg, NULL, 16);
  131.             break;
  132.         case 'q': verbose--;             break;
  133.         case 's': shared_mode++; break;
  134.         case 'v': verbose++;             break;
  135.         case 'w': write_eeprom++;         break;
  136.         case 'a': all_regs++;             break;
  137.         case '?':
  138.             errflag++;
  139.         }
  140.     if (errflag) {
  141.         fprintf(stderr, "usage:");
  142.         return 2;
  143.     }
  144.  
  145.     if (verbose)
  146.         printf(version_msg);
  147.  
  148.     el3_debug += verbose;
  149.  
  150.     dev->name = "el3";
  151.     
  152.     for (card = 0; card < 4; card++) {
  153.        printf("Looking for card %d.\n", card+1);
  154.        if (el3_probe() < 0)
  155.            break;
  156.     }
  157.     if (verbose > 3)
  158.         el3_send_packet(fake_packet, 42);
  159.  
  160.     return 0;
  161. }
  162.  
  163. int
  164. el3_probe()
  165. {
  166.     static int current_tag = 0;
  167.     static int id_port = 0x100;
  168.     unsigned short lrs_state = 0xff, i, j;
  169.     int ioaddr = 0x320;
  170.     unsigned short irq;
  171.     unsigned short eeprom_data[64];
  172.     int if_port;
  173.  
  174.     if (current_tag == 0) {
  175.         /* Select an open I/O location at 0x1*0 to do contention select. */
  176.         for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
  177.             /* Turn on I/O access permission for just this tiny region. */
  178.             if (ioperm(id_port, 2, 1) < 0) {
  179.                 perror("EtherLink III test: ioperm()");
  180.                 fprintf(stderr, "This program must be run as root.\n");
  181.                 exit(2);
  182.             }
  183.             outb(0x00, id_port);
  184.             outb(0xff, id_port);
  185.             if (inb(id_port) & 0x01)
  186.                 break;
  187.             /* Turn off I/O permission -- paranoia. */
  188.             ioperm(id_port, 2, 0);
  189.         }
  190.         if (id_port >= 0x200) {            /* GCC optimizes this test out. */
  191.             fprintf(stderr,
  192.                     " Error: No I/O port in the range 0x100..0x1f0"
  193.                     " available for 3c509 activation.\n");
  194.             exit(3);
  195.         }
  196.     }
  197.  
  198.     printf("Generating the activation sequence on port %#3.3x for card %d.\n",
  199.            id_port, current_tag + 1);
  200.     /* Check for all ISA bus boards by sending the ID sequence to the
  201.        ID_PORT.  We find cards past the first by setting the 'current_tag'
  202.        on cards as they are found.  Cards with their tag set will not
  203.        respond to subsequent ID sequences. */
  204.     outb(0x00, id_port);
  205.     outb(0x00, id_port);
  206.     outb(0x00, id_port);
  207.     for(i = 0; i < 255; i++) {
  208.         if (verbose > 3) printf("     %d %4.4x", i, lrs_state);
  209.         outb(lrs_state, id_port);
  210.         lrs_state <<= 1;
  211.         lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
  212.     }
  213.  
  214.     /* For the first probe, clear all board's tag registers. */
  215.     if (current_tag == 0)
  216.         outb(0xd0, id_port);
  217.     else                /* Otherwise kill off already-found boards. */
  218.         outb(0xd8, id_port);
  219.  
  220.     /* Read in all EEPROM data, which does contention-select.
  221.        The unique station address is in the first three words, so only
  222.        the lowest address board will stay "on-line".
  223.        3Com has the station address byte-sex "backwards" the x86, but
  224.        in the correct order for contention-select. */
  225.     for (i = 0; i < 16; i++) {
  226.         eeprom_data[i] = id_read_eeprom(id_port, i);
  227.     }
  228.  
  229.     /* Set the adaptor tag so that the next card can be found. */
  230.     outb(0xd0 + ++current_tag, id_port);
  231.  
  232.     {
  233.         unsigned short iobase = eeprom_data[8];
  234.         irq = eeprom_data[9];
  235.         if_port = iobase >> 14;
  236.         ioaddr = 0x200 + ((iobase & 0x1f) << 4);
  237.         dev->base_addr = ioaddr;
  238.         dev->name = "eth0";
  239.         irq >>= 12;
  240.     }
  241.  
  242.     if (verbose > 1)
  243.         printk("%s: ID sequence ended with %#2.2x.\n", dev->name, lrs_state);
  244.  
  245.     /* Activate the adaptor at the EEPROM location.
  246.        NOTE: Not the "current" location -- which may be different! */
  247.     printf("Activating the card at I/O address %3.3x.\n", ioaddr);
  248.     outb((ioaddr >> 4) | 0xe0, id_port);
  249.  
  250.     if (eeprom_data[7] != 0x6d50) {
  251.       fprintf(stderr,
  252.               "No ISA EtherLink III boards appear to be at index %d.\n",
  253.               current_tag);
  254.       return -1;
  255.     }
  256.     if (verbose) {
  257.       printf("EEPROM contents:");
  258.       for (i = 0; i < 16; i++)
  259.         printf("%s %4.4x", i % 16 == 0 ? "\n":"",  eeprom_data[i]);
  260.     }
  261.  
  262.     printf("\nAn ISA EtherLink III board was activated at I/O %#3.3x, IRQ %d.\n",
  263.            ioaddr, irq);
  264.  
  265. #ifdef notdef
  266.     iobase = 0x0000;
  267.     if (iobase == 0x0000) {
  268.         dev->base_addr = 0x320;
  269.         printk("%s: 3c509 has no pre-set base address, using %#x.\n",
  270.                dev->name, dev->base_addr);
  271.         outb(0xf2, id_port);
  272.     } else {
  273.         dev->base_addr = 0x200 + ((iobase & 0x1f) << 0x10);
  274.         outb(0xff, id_port);
  275.     }
  276. #endif  /* notdef */
  277.  
  278.     if (ioperm(ioaddr, 18, 1) < 0) {
  279.         perror("ethertest: ioperm()");
  280.         return 1;
  281.     }
  282.  
  283.     outw(0x0800, ioaddr + 0x0e); /* Change to EL3WINDOW(0); */
  284.     if (inw(ioaddr) != 0x6d50) {
  285.       fprintf(stderr,
  286.               "The board was activated at %#3.3x, but does not appear in"
  287.               " I/O space!\n"
  288.               "ID register at %#3.3x is %#2.2x, status register at %#3.3x"
  289.               " is %#2.2x.\n",
  290.               ioaddr, ioaddr, inw(ioaddr), ioaddr + 0x0e, inw(ioaddr + 0x0e));
  291.   } else
  292.     printk("%s: 3c509 found at %#3.3x.\n", dev->name, dev->base_addr);
  293.  
  294. #ifdef notdef
  295.     for (j = 0; j < 16; j++) {
  296.         struct timeval timeout = {0, 162};
  297.         unsigned int eeprom_in = 0;
  298.         outb(EEPROM_READ + j, id_port);
  299.         select(0,0,0,0,&timeout);
  300.         for (i = 0; i < 16; i++) {
  301.             int eeprom = inb(id_port);
  302.             if (el3_debug > 6) printf("%x", eeprom&0x01);
  303.             eeprom_in = (eeprom_in << 1) + (eeprom & 0x01);
  304.         }
  305.         printk(" EEPROM location %2x is %4.4x\n", j, eeprom_in);
  306.     }
  307.     outb(0x00, id_port);
  308.     outb(0x00, id_port);
  309. #endif
  310.  
  311.     for (j = 0; j < 8; j++) {
  312.         printk("Window %d:", j);
  313.         outw(0x0800 + j, ioaddr + 0x0e);
  314.         for (i = 0; i < 16; i+=2)
  315.             printk(" %4.4x", inw(ioaddr + i));
  316.         printk(".\n");
  317.     }
  318.     printf("   Done card %d.\n", current_tag);
  319.     return 0;
  320. }
  321.  
  322. int
  323. el3_send_packet(void *pkt, int len)
  324. {
  325.     int ioaddr = dev->base_addr;
  326.  
  327.     outw(0x0801, ioaddr + EL3_CMD);
  328.     if (el3_debug > 2) {
  329.         printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n",
  330.                dev->name, len, inw(ioaddr + EL3_CMD));
  331.     }
  332.  
  333.     outw(0x5800, ioaddr + EL3_CMD);
  334.     outw(0x4800, ioaddr + EL3_CMD);
  335.  
  336.     outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
  337.     outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */
  338.  
  339.     /* Avoid timer-based retransmission conflicts. */
  340.     dev->tbusy=1;
  341.  
  342.     /* Put out the doubleword header... */
  343.     outw(len, ioaddr + TX_FIFO);
  344.     outw(0x00, ioaddr + TX_FIFO);
  345.     if (el3_debug > 4)
  346.         printk("        Started queueing packet, FIFO room %d status %4.4x.\n",
  347.                inw(ioaddr + TX_FREE), inw(ioaddr+EL3_CMD));
  348.     /* ... and the packet rounded to a doubleword. */
  349.     outsl(ioaddr + TX_FIFO, (void *)pkt, (len + 3) >> 2);
  350.     
  351.     if (el3_debug > 4)
  352.         printk("        Finished queueing packet, FIFO room remaining %d.\n",
  353.                inw(ioaddr + TX_FREE));
  354.     dev->trans_start = jiffies;
  355.     
  356.     if (inw(ioaddr + TX_FREE) > 1536) {
  357.         dev->tbusy=0;
  358.     } else
  359.         /* Interrupt us when the FIFO has room for max-sized packet. */
  360.         outw(0x9000 + 1536, ioaddr + EL3_CMD);
  361.  
  362.     if (el3_debug > 4)
  363.         printk("        Checking packet queue, FIFO room %d status %2.2x.\n",
  364.                inw(ioaddr + TX_FREE), inb(ioaddr+0xb));
  365.  
  366.     return 0;
  367. }
  368.  
  369. /* Read a word from the EEPROM when in the ISA ID probe state. */
  370. static ushort id_read_eeprom(int id_port, int index)
  371. {
  372.     int bit, word = 0;
  373.  
  374.     /* Issue read command, and pause for at least 162 us. for it to complete.
  375.        Assume extra-fast 16Mhz bus. */
  376.     outb(EEPROM_READ + index, id_port);
  377.  
  378.     /* Pause for at least 162 us. for the read to take place. */
  379.     usleep(162);
  380.  
  381.     for (bit = 15; bit >= 0; bit--)
  382.         word = (word << 1) + (inb(id_port) & 0x01);
  383.  
  384.     if (verbose > 3)
  385.         printk("  3c509 EEPROM word %d %#4.4x.\n", index, word);
  386.  
  387.     return word;
  388. }
  389.  
  390. /*
  391.  * Local variables:
  392.  *  compile-command: "cc -N -O -Wall -o el3-diag el3.c"
  393.  *  tab-width: 4
  394.  *  c-indent-level: 4
  395.  * End:
  396.  */
  397.