/* atlantic.c: Diagnostic program for AT/LANTIC DP83905 ethercards. */
/*
    Written 1993 by Donald Becker. This is unreleased alpha test code.
    This version is a application for the Linux operating system, and is
    covered by same Gnu Public License that covers that work.
    
    This diagnotic routine should work with many 8390-based ethernet boards.

    The Author may be reached as becker@super.org or
    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/

static char *version =
    "atlantic.c:v0.00 3/26/93 Donald Becker (becker@super.org)\n";

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <asm/io.h>
/* #include "8390.h" */


#define NS_CMD	 (dev->base_addr)
#define NS_BASE	 (dev->base_addr)
#define NS_DATAPORT	0x10	/* NatSemi-defined port window offset. */
#define NS_RESET	0x1f	/* Issue a read to reset, a write to clear. */

void write_EEPROM(int port_base, int regA, int regB, int regC);
struct option longopts[] = {
 /* { name  has_arg  *flag  val } */
    {"base-address", 1, 0, 'p'}, /* Give help */
    {"help",       0, 0, 'h'},	/* Give help */
    {"interface",  0, 0, 'f'},	/* Interface number (built-in, AUI) */
    {"irq",	   1, 0, 'i'},	/* Interrupt number */
    {"ne2000",	   1, 0, 'N'},	/* Switch to NE2000 mode */
    {"verbose",    0, 0, 'v'},	/* Verbose mode */
    {"version",    0, 0, 'V'},	/* Display version number */
    {"wd8013",	   1, 0, 'W'},	/* Switch to WD8013 mode */
    {"write-EEPROM", 1, 0, 'w'},/* Write th EEPROMS with the specified vals */
    { 0, 0, 0, 0 }
};


/*  Probe for various non-shared-memory ethercards.
   
   NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
   buffer memory space.  NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
   the SAPROM, while other supposed NE2000 clones must be detected by their
   SA prefix.

   Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
   mode results in doubled values, which can be detected and compansated for.

   The probe is also responsible for initializing the card and filling
   in the 'dev' and 'ei_status' structures.

   We use the minimum memory size for some ethercard product lines, iff we can't
   distinguish models.  You can increase the packet buffer size by setting
   PACKETBUF_MEMSIZE.  Reported Cabletron packet buffer locations are:
	E1010   starts at 0x100 and ends at 0x2000.
	E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
	E2010	 starts at 0x100 and ends at 0x4000.
	E2010-x starts at 0x100 and ends at 0xffff.  */

int
main(int argc, char *argv[])
{
    int port_base = 0x300, irq = -1;
    int errflag = 0, verbose = 0, shared_mode = 0, write_eeprom = 0, interface = -1;
    int c;
    extern char *optarg;

    while ((c = getopt(argc, argv, "f:i:p:svw")) != -1)
	switch (c) {
	case 'f':
	    interface = atoi(optarg);
	    break;
	case 'i':
	    irq = atoi(optarg);
	    break;
	case 'p':
	    port_base = strtol(optarg, NULL, 16);
	    break;
	case 's': shared_mode++; break;
	case 'v': verbose++;	 break;
	case 'w': write_eeprom++;	 break;
	case '?':
	    errflag++;
	}
    if (errflag) {
	fprintf(stderr, "usage:");
	return 2;
    }

    if (verbose)
	printf(version);
    if (ioperm(port_base, 16, 1)) {
	perror("io-perm");
	return 1;
    }
    
    printf("Checking the ethercard at %#3x.\n", port_base);
    {
	int regA, regB;
	char *io_base[8] = {"0x300", "none",};
	int index2irq[8] = {3, 4, 5, 9, 10, 11, 12, 15};
	int irq2index[16] = {-1, -1, 3, 0, 1, 2, -1, -1,
				 -1, 3, 4, 5, 6, -1, -1, 7};
	char *interface_name[4] = {"10baseT", "Thinnet", "AUI", "10baseT++"};
	regA = inb(port_base + 0x0A);
	printf(" Register A is %#02x:  I/O base @ %s,  IRQ %d,  %s mode.\n",
	       regA, io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07],
	       regA & 0x80 ? "8013" : "NE2000");
	if (irq >= 0  &&  irq < 16  && irq2index[irq] > 0) {
	    regA &= 0xc7;
	    regA |= (irq2index[irq]<<3);
	    if (shared_mode) regA |= 0x80;
	    printf(" Setting register A to %#02x  (IRQ %d).\n", regA, irq);
	    inb(port_base + 0x0A);
	    outb(regA, port_base + 0x0A);
	    printf(" Register A is now %#02x.\n", inb(port_base + 0x0A));
	}
	regB = inb(port_base + 0x0B);
	printf(" Register B is %#02x:  Interface %s.\n", regB,
	       interface_name[regB & 0x03]);
	if (interface >= 0) {
	    regB &= 0x5C;
	    regB |= (interface & 0x3);
	    printf(" Setting register B to %#02x.\n", regB);
	    inb(port_base + 0x0B);
	    outb(regB, port_base + 0x0B);
	    printf(" Register B is now %#02x.\n", inb(port_base + 0x0B));
	}
	if (write_eeprom) {
	    printf("Writing EEPROM at %#3x with %#02x %#02x %#02x.\n",
		   port_base, regA, regB, 0x30);
	    write_EEPROM(port_base, regA, regB, 0x30);
	}
    }
    return 0;
}

/* Write the A, B, and C configuration registers into the EEPROM at PORT_BASE. */
void
write_EEPROM(int port_base, int regA, int regB, int regC)
{
    int currB = inb(port_base + 0x0B) & (~0x04) ;
    outb(currB | 0x80, port_base + 0x0B);
    inb(port_base + 0x0B);
    outb(regA, port_base + 0x0B);
    outb(regB, port_base + 0x0B);
    outb(regC, port_base + 0x0B);
    return;
}

/*
 * Local variables:
 *  compile-command: "gcc -Wall -O6 -o atlantic atlantic.c"
 * End:
 */
