/* el21.c: Diagnostic program for Cabletron E2100 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.
    
    The Author may be reached as becker@super.org or
    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <asm/io.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

/* #include "8390.h" */


#define printk printf

/* Offsets from the base_addr. */
#define E21_NIC_OFFSET  0       /* Offset to the 8390 NIC. */
#define E21_ASIC		0x10
#define E21_MEM_ENABLE	0x10	/* Read from these, and the low 4 bits of */
#define E21_MEM_BASE	0x11	/* the next outb() address is used to set */
#define E21_IRQ_LOW		0x12	/* the cooresponding register. */
#define E21_IRQ_HIGH	0x14
#define  E21_MEM_ON		0x05	/* Enable memory in 16 bit mode. */
#define  E21_MEM_ON_8	0x07	/* Enable memory in  8 bit mode. */
#define E21_SAPROM      0x18    /* Offset to station address data. */
#define ETHERCARD_TOTAL_SIZE    0x20


extern inline void mem_on(short port, volatile char *mem_base,
						  unsigned char start_page )
{
	/* This is a little weird: set the shared memory window by doing a
	   read.  The low address bits specify the starting page. */
	mem_base[start_page];
	inb(port + E21_MEM_ENABLE);
	outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
}

extern inline void mem_off(short port)
{
	inb(port + E21_MEM_ENABLE);
	outb(0x00, port + E21_MEM_ENABLE);
}

struct option longopts[] = {
 /* { name  has_arg  *flag  val } */
    {"base-address", 1, 0, 'p'}, /* Give help */
    {"help",       0, 0, 'h'},	/* Give help */
    {"force",  0, 0, 'f'},	/* Force an operation, even with bad status. */
    {"irq",	   1, 0, 'i'},	/* Interrupt number */
    {"verbose",    0, 0, 'v'},	/* Verbose mode */
    {"version",    0, 0, 'V'},	/* Display version number */
    { 0, 0, 0, 0 }
};


/*  Probe for E2100 series ethercards.
   
   E21xx boards have a "PAXI" located in the 16 bytes above the 8390.
   The "PAXI" reports the station address when read, and has an wierd
   address-as-data scheme to set registers when written.
*/

int
main(int argc, char *argv[])
{
    int port_base = 0x300, irq = -1;
    int errflag = 0, verbose = 0, shared_mode = 0, force = 0;
    int c, memfd, ioaddr, i;
    extern char *optarg;
    caddr_t shared_mem;

    while ((c = getopt(argc, argv, "fi:p:svw")) != -1)
	switch (c) {
	case 'f':  force++; 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 '?':
	    errflag++;
	}
    if (errflag) {
	fprintf(stderr, "usage:");
	return 2;
    }

    if (verbose)
	printf(version);

    if (ioperm(port_base, 32, 1)
	|| ioperm(0x80, 1, 1)) { 		/* Needed for SLOW_DOWN_IO. */
	perror("io-perm");
	fprintf(stderr, " You must be 'root' to run hardware diagnotics.\n");
	return 1;
    }
    memfd = open("/dev/kmem", O_RDWR);
    if (memfd < 0) {
	perror("/dev/kmem (shared memory)");
	return 2;
    }
    shared_mem = mmap((caddr_t)0xd0000, 0x8000, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, 0xd0000);
    printf("Shared memory at %#x.\n", (int)shared_mem);
    
    ioaddr = port_base;
    printf("Checking the ethercard at %#3x.\n", port_base);
    {	
	int cmdreg;
	int oldcmdreg = inb(ioaddr);
	outb_p(0x21, ioaddr);
	cmdreg = inb(ioaddr);
	if (cmdreg != 0x21  &&  cmdreg != 0x23) {
	    outb(oldcmdreg, ioaddr); 		/* Restore the old values. */
	    printk("  Failed initial E2100 probe, value %2.2x.\n", cmdreg);
	    if (! force) return 0;
	} else
	    printk("  Passed initial E2100 probe, value %2.2x.\n", cmdreg);
    }

    printk("8390 registers:");
    for (i = 0; i < 16; i++)
	printk(" %02x", inb(ioaddr + i));
    printk("\nPAXI registers:");
    for (i = 0; i < 16; i++)
	printk(" %02x", inb(ioaddr + 16 + i));
    printk("\n");

    inb_p(ioaddr + E21_MEM_BASE);
    inb_p(ioaddr + E21_MEM_BASE);
    outb_p(0, ioaddr + E21_ASIC + ((0xd000 >> 13) & 7));

    {
	volatile ushort *mem = (ushort*)shared_mem;
	int page;
	for (page = 0; page < 0x41; page++) {
	    mem_on(ioaddr, shared_mem, page);
	    mem[0] = page << 8;
	    mem_off(ioaddr);
	}

	printk("Read %04x %04x.\n", mem[0], mem[1]);
	for (page = 0; page < 256; page++) {
	    int i;
	    mem_on(ioaddr, shared_mem, page);
	    printk("Page %#x:", page);
	    for (i = 0; i < 16; i++)
		printk(" %04x", mem[i*128]);
	    printk("\n");
	    /*printk(" %04x%s", mem[0], (page & 7) == 7 ? "\n":"");*/
	    mem_off(ioaddr);
	}
	printk("\n");
	mem[0] = 0x5742;
	printk("Read %04x %04x.\n", mem[0], mem[1]);
	printk("Read %04x %04x.\n", mem[2], mem[3]);
    }
    /* do_probe(port_base);*/
    return 0;
}


/*
 * Local variables:
 *  compile-command: "gcc -Wall -O6 -N -o e21 e21.c"
 * End:
 */
