
/*
 * tokenset v0.10 for Intel TokenExpress ISA Adapters.
 *
 * This should do everything that TOKENSET.EXE (from InteL) 
 * does under DOS.  Its got some bugs left, I know.  Fix them 
 * if you like.  Changes may not always take and the DMA might
 * get interpretted improperly.  Shrug.  I don't really understand
 * the chip to begin with.  I'm only guessing really.  
 *
 * If called with no parameters, it simply reads out the current
 * settings of the card and interprets them.  You can also
 * give options to change them (hopefully).  
 *
 * See the -h option.
 *
 * Compile with "gcc -Wall -Wstrict-prototypes -O2 -o tokenset tokenset.c"
 *
 * Distributed under the GNU Public License (www.fsf.org).
 *
 * Written by Adam Fritzler (mid@auk.cx), maintainer of the Linux tms380tr
 * device driver, which should also work with any card this works
 * with (assuming this works at all).
 *
 * 27Nov1999
 * 
 */
#include <stdio.h>
#include <asm/io.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>

static char version[]="tokenset v0.10 for Intel TokenExpress ISA adapters, (c)1999 Adam Fritzler (mid@auk.cx)";

/* Token ring adapter I/O addresses for normal mode. */
#define SIFDAT                  0L      /* SIF/DMA data. */
#define SIFINC                  2L      /* IO Word data with auto increment. */
#define SIFINH                  3L      /* IO Byte data with auto increment. */
#define SIFADR                  4L      /* SIF/DMA Address. */
#define SIFCMD                  6L      /* SIF Command. */
#define SIFSTS                  6L      /* SIF Status. */
#define SIFACL                  8L      /* SIF Adapter Control Register. */
#define SIFADD                  10L     /* SIF/DMA Address. */
#define SIFADX                  12L
#define DMALEN                  14L     /* SIF DMA length. */
#define POSREG                  16L     /* Adapter Program Option Select (POS)
                                         * Register: base IO address + 16 byte.
                                         */
#define POSREG_2                24L     /* only for TR4/16+ adapter
                                         * base IO address + 24 byte.
                                         */

int irqs[] = {9, 10, 11, 12, 15, 3, 5, 7, 0xff};
int dmas[] = {5, 6, 7, 0, 1, 3, -1, 0xff};
unsigned short ios[] = {0x0a20, 1, 0x0a24, 1, 0x0a50, 1, 0x0a54, 0xff};

/*
 * This function tests if there's a TMS380 processor installed at the
 * given I/O address. Return negative if no adapter at IO addr.
 */
int tms380tr_probe(int ioaddr)
{
  unsigned char old, chk1, chk2;
  
  old = inb(ioaddr + SIFADR);     /* Get the old SIFADR value */
  
  chk1 = 0;       /* Begin with check value 0 */
  do {
    /* Write new SIFADR value */
    outb(chk1, ioaddr + SIFADR);
    
    /* Read, invert and write */
    chk2 = inb(ioaddr + SIFADD);
    chk2 ^= 0x0FE;
    outb(chk2, ioaddr + SIFADR);
    
    /* Read, invert and compare */
    chk2 = inb(ioaddr + SIFADD);
    chk2 ^= 0x0FE;
    if(chk1 != chk2)
      return (-1);    /* No adapter */
    
    chk1 -= 2;
  } while(chk1 != 0);     /* Repeat 128 times (all byte values) */
  
  /* Restore the SIFADR value */
  outb(old, ioaddr + SIFADR);
  
  return (1); /* card found */
}


unsigned short baseaddr;
unsigned short tmsaddr;

#if 0
/*
 * Prints out MAC address.  SHOULD NEVER BE USED ON AN OPEN ADAPTER!!!
 * WILL CRASH OPEN ADAPTER AND PROBABLY SCRIBBLE IN HOST MEMORY!!!
 *
 * Not used yet.
 *
 */
void tms380tr_read_addr(void)
{
        int i;
        /* Address: 0000:0000 */
        outw(0, tmsaddr + SIFADX);
        outw(0, tmsaddr + SIFADR);

        /* Read six byte MAC address data */
        for(i = 0; i < 6; i++)
	  printf("%02x ", inw(tmsaddr + SIFINC)>>8);

        return;
}
#endif

/*
 * The MB620228 has 8 index registers.  These get and set them.
 */
void setreg(unsigned char reg, unsigned char val)
{
  outb(reg, baseaddr+0x02);
  outb(val, baseaddr+0x03);
  return;
}

unsigned char getreg(unsigned char reg)
{
  outb(reg, baseaddr+0x02);
  return inb(baseaddr+0x03);
}

/*
 * This does a simple probe to find the MB620228 config/glue chip.
 * Note that this has nothing to do with finding the actual
 * TMS380C16 except indirectly (the controller address depends
 * on the MB620228 address). The address is returned as both
 * the return value and in baseaddr. 
 *
 */
unsigned short configprobe(void)
{
  int i,j;
  unsigned short probelist[] = {
    0x0a20,  /* chip at 0xa30 */
    0x0a24,  /*         0xa40 */
    0x0a50,  /*         0xa60 */
    0x0a54,  /*         0xa70 */
    0}; 

  for (j = 0; probelist[j]!=0; j++)
    {
      baseaddr = probelist[j]; /* {get,set}reg() need this set */

      /* initialization procedure -- really needed? */
      for (i = 0; i < 5; i++)
	inb(baseaddr+0x02);
      outb(0x06, baseaddr+0x02);
      i = inb(baseaddr+0x02);
      outb(i, baseaddr+0x02);
      
      /* read/write test -- register 0x06 only saves the lower nibble! */
      for (i = 10; i > 0; i--)
	{
	  setreg(0x06, 0x42);
	  if ((getreg(0x06)&0xf) != ((0x42)&0xf))
	    break;
	  setreg(0x06, 0x84);
	  if ((getreg(0x06)&0xf) != ((0x84)&0xf))
	    break;
	}
      if (i==0)
	return baseaddr;
    }
  return 0;
}

/*
 * Determines TMS380 address based on MB620228 address.
 */
unsigned short gettmsaddr(unsigned short configaddr)
{
  switch (configaddr)
    {
    case 0x0a20: return 0x0a30;
    case 0x0a24: return 0x0a40;
    case 0x0a50: return 0x0a60;
    case 0x0a54: return 0x0a70;
    default: return 0;
    }
  return 0;
}

int main(int argc, char **argv)
{
  int i;
  unsigned char regs[8];
  char c;
  unsigned char newio = 0xff;
  unsigned char newdma = 0xff;
  unsigned char newirq = 0xff;
  unsigned char newspeed = 0xff;
  int changed = 0;

  while (1)
    {
      int option_index = 0;
      static struct option long_options[] = {
	{"io", 1, 0, 0},
	{"dma", 1, 0, 0},
	{"irq", 1, 0, 0},
	{"version", 0, 0, 0},
	{"help", 0, 0, 0},
	{"speed", 1, 0, 0},
	{0,0,0,0}
      };
      c = getopt_long(argc, argv, "hv", long_options, &option_index);
      if (c == -1)
	break;
      switch(c)
	{
	case 0:
	  switch(option_index)
	    {
	    case 0: /* io */
	      {
		if ((atoi(optarg) > 3) || atoi(optarg) < 0)
		  {
		    fprintf(stderr, "Invalid IO address specified\n");
		    return -1;
		  }
		newio = atoi(optarg);
	      }
	      break;
	    case 1: /* dma */
	      {
		int idx;
		for (idx = 0; dmas[idx] != 0xff; idx++)
		  {
		    if (dmas[idx]==atoi(optarg))
		      break;
		  }
		if (dmas[idx] == 0xff)
		  {
		    fprintf(stderr, "Invalid DMA specified\n");
		    return -1;
		  }
		newdma = idx;
	      }
	      break;
	    case 2: /* irq */
	      {
		int idx;
		for (idx = 0; irqs[idx] != 0xff; idx++)
		  {
		    if (irqs[idx]==atoi(optarg))
		      break;
		  }
		if (irqs[idx] == 0xff)
		  {
		    fprintf(stderr, "Invalid IRQ specified\n");
		    return -1;
		  }
		newirq = idx;
	      }
	      break;
	    case 3: /* version */
	      goto version;
	    case 4: /* help */
	      goto help;
	    case 5: /* speed */
	      if (atoi(optarg) == 4)
		newspeed = 0;
	      else if (atoi(optarg) == 16)
		newspeed = 1;
	      else
		{
		  fprintf(stderr, "Invalid speed specified -- only 4 and 16Mb/sec operation supported\n");
		  return -1;
		}
	      break;
	    }
	  break;
	version:
	case 'v':
	  printf("%s\n", version);
	  return 0;
	help:
	case 'h':
	  printf("%s\nIf called with no options, prints current settings.\n", version);
	  printf("!!!!WARNING: DO NOT RUN ON AN ACTIVE CARD! !!!!\n");
	  printf("Options:\n");
	  printf("\t--io\t\tSets I/O Address:\n");
	  printf("\t    \t\t\t=0 0x0A20 / 0x0A30\n");
	  printf("\t    \t\t\t=1 0x0A24 / 0x0A40\n");
	  printf("\t    \t\t\t=2 0x0A50 / 0x0A60\n");
	  printf("\t    \t\t\t=3 0x0A54 / 0x0A70\n");
	  printf("\t--dma\t\tSets DMA channel\n");
	  printf("\t--irq\t\tSets IRQ line\n");
	  printf("\t--speed\t\tSets ring speed (=4 or =16)\n");
	  printf("!!!!WARNING: DO NOT RUN ON AN ACTIVE CARD! !!!!\n");
	  printf("\n");
	  return 0;
	}
    }

  if (iopl(3)<0)
    {
      printf("Could not get permission.  Must be root or setuid.\n");
      return -1;
    }

  if ((baseaddr = configprobe()) == 0)
    {
      fprintf(stderr, "No cards found.\n");
      return -1;
    }
  else
    printf("Found card at 0x%03x.\n", baseaddr);
  
  
  printf("Configuration register values: ");
  for (i = 0; i < 8; i++)
    printf("%02x ", regs[i] = getreg(i));
  printf("\n");

  if ((tmsaddr = gettmsaddr(baseaddr))==0)
    {
      fprintf(stderr, "Config chip located at invalid base address; probably a prober screwup (baseaddr=0x%03x).\n", baseaddr);
      return -1;
    }

  if (tms380tr_probe(tmsaddr)!=-1)
    printf("TMS380 chip found at 0x%04x\n", tmsaddr);
  else
    {
      fprintf(stderr, "TMS380 chip probe failed.\n");
      return -1;
    }

  if (newspeed != 0xff)
    {
      if ((newspeed == 0)&&(regs[0x03]&0x1))
	regs[0x03] ^= 0x01;
      changed++;
    }

  if (newirq != 0xff)
    {
      regs[0x02] &= 0xf8; /* kill current irq */
      regs[0x02] |= newirq; /* add in new one */
      changed++;
    }

  if (newdma != 0xff)
    {
      regs[0x02] &= 0x07; 
      regs[0x02] |= newdma<<3;
      changed++;
    }
  if (newio != 0xff)
    {
      regs[0x03] &= 0xf9;
      regs[0x03] |= newio<<1;
      changed++;
    }

  if (changed)
    {
      printf("Writing new values: ");
      for (i = 0; i < 8; i++)
	{
	  printf("%02x ", regs[i]);
	  setreg(i, regs[i]);
	}
      printf("\n");
    }

  
  if (changed)
    printf("\nNew Settings:\n");
  else
    printf("\nCurrent Settings:\n");

  /* IO address */
  printf("\tI/O:\n");
  printf("\t\tConfig: 0x%03x-0x%03x\n", baseaddr, baseaddr+4);
  printf("\t\tTMS380: 0x%03x-0x%03x\n", tmsaddr, tmsaddr+0x1f);

  /* IRQ */
  printf("\tIRQ: %d\n", irqs[regs[0x02]&0x7]);
  

  /* DMA */
  printf("\tDMA: %d %s\n", (regs[0x02]>>3)&0x1f, (dmas[(regs[0x02]>>3)&0x7]>0)?"(DMA mode)":"(PIO/pDMA mode)");

  /* Ring Speed */
  printf("\tRing Speed: %dMb/sec\n", (regs[0x03] & 0x01)?16:4);

  /* ROM address */
  printf("\tRPL ROM Address: (not implemented)\n");

  /* MAC address -- not sure how this goes on Intel cards yet */
  printf("\tRing Station Node Address: ");
#if 0
  tms380tr_read_addr();
  printf("\n");
#else
  printf("(not implemented)\n");
#endif

  printf("\n");

  return (0);
  
}

