/* el3setup.c: Diagnostic program for 3c5*9 ethercards.

   Written 1993 by Donald Becker.

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

static char *version =
    "el3setup.c:v0.04 9/2/93 Donald Becker (becker@super.org)\n";

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <asm/io.h>
#include <sys/time.h>

#define port_write_l(port,buf,nr) \
__asm__("cld;rep;outsl": :"d" (port),"S" (buf),"c" (nr):"cx","si")
inline static void outw( unsigned short value, unsigned short port )
{
   __asm__ volatile ( "outw %0,%1"
		      : :"a" ((unsigned short) value),
		      "d" ((unsigned short) port) );
}

inline static unsigned int inw(unsigned short port)
{
	unsigned int _v;
__asm__ __volatile__ ("inw %w1,%w0"
		:"=a" (_v):"d" (port),"0" (0));
	return _v;
}

struct option longopts[] = {
 /* { name  has_arg  *flag  val } */
    {"base-address", 1, 0, 'p'},
    {"all",	   0, 0, 'a'},	/* Print all registers. */
    {"help",       0, 0, 'h'},	/* Give help */
    {"interface",  0, 0, 'f'},	/* Interface number (built-in, AUI) */
    {"irq",	   1, 0, 'i'},	/* Interrupt number */
    {"verbose",    0, 0, 'v'},	/* Verbose mode */
    {"version",    0, 0, 'V'},	/* Display version number */
    {"write-EEPROM", 1, 0, 'w'},/* Write th EEPROMS with the specified vals */
    { 0, 0, 0, 0 }
};

#define printk printf

#ifdef EL3_DEBUG
int el3_debug = EL3_DEBUG;
#else
int el3_debug = 6;
#endif

/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
#define ID_PORT 0x100
#define  EEPROM_READ 0x80

/* Register window 1 offsets, used in normal operation. */
#define TX_FREE 0x0C
#define TX_STATUS 0x0B
#define TX_FIFO 0x00
#define RX_FIFO 0x00

int el3_probe(void);

struct device {
    char *name;
    short base_addr;
    int tbusy;
    int trans_start;
} devs, *dev;

int jiffies;
unsigned char fake_packet[100];

int el3_send_packet(void *pkt, int len);



int
main(int argc, char **argv)
{
    int port_base = -1, irq = -1;
    int errflag = 0, verbose = 0, shared_mode = 0;
    int write_eeprom = 0, interface = -1, all_regs = 0;
    int c, longind;
    extern char *optarg;
    dev = &devs;

    while ((c = getopt_long(argc, argv, "af:i:p:svw", longopts, &longind))
	   != -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 'a': all_regs++;		 break;
	case '?':
	    errflag++;
	}
    if (errflag) {
	fprintf(stderr, "usage:");
	return 2;
    }

    if (verbose)
	printf(version);

    el3_debug += verbose;

    dev->name = "el3";
    
    if (ioperm(ID_PORT, 2, 1) < 0) {
	perror("ethertest: ioperm()");
	return 1;
    }
    el3_probe();
    if (verbose > 1)
	el3_send_packet(fake_packet, 42);

    return 0;
}

int
el3_probe()
{
    unsigned short lrs_state = 0xff, i, j;
    short iobase = 0;
    int eeprom_data = 0;
    int ioaddr = 0x320;

    /* Send the ID sequence to the ID_PORT. */
    outb(0x00, ID_PORT);
    outb(0x00, ID_PORT);
    outb(0x00, ID_PORT);
    for(i = 0; i < 255; i++) {
	if (el3_debug > 6) printf("  %d %4.4x", i, lrs_state);
	outb(lrs_state, ID_PORT);
	lrs_state <<= 1;
	lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
    }

    if (el3_debug > 1)
	printk("\n%s: ID sequence ended with %#2.2x.\n", dev->name, lrs_state);

    /* The current Space.c initialization makes it difficult to have more
       than one adaptor initialized.  Send me email if you have a need for
       multiple adaptors. */

    /* Select a single adaptor, while read in EEPROM data. */

    /* Activate the adaptor at the pre-programmed location. */

    iobase = 0x0000;
    if (iobase == 0x0000) {
	dev->base_addr = 0x320;
	printk("%s: 3c509 has no pre-set base address, using %#x.\n",
	       dev->name, dev->base_addr);
	outb(0xf2, ID_PORT);
    } else {
	dev->base_addr = 0x200 + ((iobase & 0x1f) << 0x10);
	outb(0xff, ID_PORT);
    }

    if (ioperm(dev->base_addr, 18, 1) < 0) {
	perror("ethertest: ioperm()");
	return 1;
    }

    outw(0x0800, ioaddr + 0x0e);

    if (inw(dev->base_addr) == 0x6d50) {
	printk("%s: 3c509 found at %#3.3x.\n", dev->name, dev->base_addr);
    } else {
	printk("%s: 3c509 not found at %#3.3x, status %4.4x.\n",
	       dev->name, dev->base_addr, inw(dev->base_addr));
    }

    for (j = 0; j < 16; j++) {
	struct timeval timeout = {0, 162};
	eeprom_data = 0;
	outb(EEPROM_READ + j, ID_PORT);
	select(0,0,0,0,&timeout);
	for (i = 0; i < 16; i++) {
	    int eeprom = inb(ID_PORT);
	    if (el3_debug > 6) printf("%x", eeprom&0x01);
	    eeprom_data = (eeprom_data << 1) + (eeprom & 0x01);
	}
	printk(" EEPROM location %2x is %4.4x\n", j, eeprom_data);
    }
    outb(0x00, ID_PORT);
    outb(0x00, ID_PORT);
    
    for (j = 0; j < 7; j++) {
	printk("Window %d:", j);
	outw(0x0800 + j, ioaddr + 0x0e);
	for (i = 0; i < 16; i+=2)
	    printk(" %4.4x", inw(ioaddr + i));
	printk(".\n");
    }

    return 0;
}

int
el3_send_packet(void *pkt, int len)
{
    int ioaddr = dev->base_addr;

    outw(0x0801, ioaddr + EL3_CMD);
    if (el3_debug > 2) {
	printk("%s: el3_start_xmit(lenght = %d) called, status %4.4x.\n",
	       dev->name, len, inw(ioaddr + EL3_CMD));
    }

    outw(0x5800, ioaddr + EL3_CMD);
    outw(0x4800, ioaddr + EL3_CMD);

    outw(0x78ff, ioaddr + EL3_CMD); /* Allow all status bits to be seen. */
    outw(0x7098, ioaddr + EL3_CMD); /* Set interrupt mask. */

    /* Avoid timer-based retransmission conflicts. */
    dev->tbusy=1;

    /* Put out the doubleword header... */
    outw(len, ioaddr + TX_FIFO);
    outw(0x00, ioaddr + TX_FIFO);
    if (el3_debug > 4)
	printk("        Started queueing packet, FIFO room %d status %4.4x.\n",
	       inw(ioaddr + TX_FREE), inw(ioaddr+EL3_CMD));
    /* ... and the packet rounded to a doubleword. */
    port_write_l(ioaddr + TX_FIFO, (void *)pkt, (len + 3) >> 2);
    
    if (el3_debug > 4)
	printk("        Finished queueing packet, FIFO room remaining %d.\n",
	       inw(ioaddr + TX_FREE));
    dev->trans_start = jiffies;
    
    if (inw(ioaddr + TX_FREE) > 1536) {
	dev->tbusy=0;
    } else
	/* Interrupt us when the FIFO has room for max-sized packet. */
	outw(0x9000 + 1536, ioaddr + EL3_CMD);

    if (el3_debug > 4)
	printk("        Checking packet queue, FIFO room %d status %2.2x.\n",
	       inw(ioaddr + TX_FREE), inb(ioaddr+0xb));

    return 0;
}

/*
 * Local variables:
 *  compile-command: "cc -N -O -Wall -o el3 el3.c"
 * End:
 */
