#include "wdcnfg.h"
/*
	this code is based on assembly code by George Kalwitz, as part of the
	SMC sample drivers disk.

	version 0.1 29-jan-93
	re-written for Linux in C, and Microchannel stuff removed
	by Gregg Weber

	version 0.2 31-jan-93
	added check for nic chip type
*/

/*
	get board id information.

	returns 0 if bad board
	else returns 32 bit word with bits encoding information about
	the board. See getcnfg.h for definitions of bits.
*/
extern unsigned int inb_p();
extern void outb_p();

/************************************************************************
  this determines if the nic chip is a 690 or 8390.
  there are programming differences so, the driver has to know
  which chip is there.
*************************************************************************/
int is_nic_690(int ioaddr)
{
  int i,oldcr,enhval,enhval2;
  int is690 = 0;
  oldcr = inb_p(ioaddr + 0x10) & 0xfb; /* get command register, mask txp */
  outb_p((oldcr & 0x3f) | 0x80,ioaddr + 0x10); /* select page 2 */
  enhval = inb_p(ioaddr + 0x17); /* get enh */
  outb_p(enhval + 0x18,ioaddr + 0x17); /* write 0x18 to enh */
  i = inb_p(ioaddr + 0x10); /* put something else on bus */
  enhval2 = inb_p(ioaddr + 0x17) & 0x18; /* get new enh */
  if (enhval2 == 0x18) is690 = 1;
  /* restore stuff */
  outb_p(enhval,ioaddr + 0x17); /* restore enh */
  outb_p(oldcr,ioaddr + 0x10); /* restore cr */
  return(is690);
}



unsigned long smc_get_board_id(int ioaddr)
{
  int alias = 1;
  unsigned long board_id = 0;
  int board_rev,i;
  unsigned char temp1,temp2,temp7;
  /* rev number = 0 means board is broken */
  if ((board_rev = (inb_p(ioaddr+0xe) & 0x1e) >> 1) == 0) return(0);
  /* see if there is register aliasing */
  /* only check regs 1,2,3,4,7 - some ASICs don't have regs 5 and 6 */
  for (i = 1; i <= 5; i++) {
    if (inb_p(ioaddr+i) != inb_p(ioaddr+i+8)) {
      alias = 0;
      break;
    }
  }
  if (inb_p(ioaddr+7) != inb_p(ioaddr+7+8)) alias = 0;
  /* if no aliasing, check some more stuff */
  if (!alias) {
    /* see if there is an interface chip */
    temp7 = inb_p(ioaddr+7); /* save reg just in case */
    /* see if we can write and read some values in gp2 register */
    outb_p(0x35,ioaddr+7); /* write something */
    temp2 = inb_p(ioaddr + 8); /* put something else on bus */
    if (inb_p(ioaddr+7) == 0x35) {
      outb_p(0x3a,ioaddr+7); /* try another value */
      temp2 = inb_p(ioaddr + 8); /* put something else on bus */
      if (inb_p(ioaddr+7) == 0x3a) board_id |= INTERFACE_CHIP;
    }
    outb_p(temp7,ioaddr+7); /* restore reg just in case */
    /* is board 16 bit? */
    temp7 = inb_p(ioaddr+1); /* save value */
    temp1 = (temp7 & 1) ^ 1;
    outb_p(temp1,ioaddr+1); /* try to flip the 16-bit bit */
    temp2 = inb_p(ioaddr); /* put something else on bus */
    if (temp1 == (inb_p(ioaddr+1) & 1)) {
      outb_p(temp7 & 0xfe,ioaddr+1); /* restore it and clear bit 0 */
      board_id |= BOARD_16BIT;
      /* is 16 bit board in 16 bit slot? */
      if ((inb_p(ioaddr+1) & 1) == 1) board_id |= SLOT_16BIT;
    }
    else {
      outb_p(temp7,ioaddr+1); /* restore orig value */
    }
    /* find the media type */
    if ((inb_p(ioaddr+0xe) & 1) == 1) board_id |= ETHERNET_MEDIA;
    else {
      if (board_rev == 1) board_id |= STARLAN_MEDIA;
      else board_id |= TWISTED_PAIR_MEDIA;
    }
    if (board_rev >= 2) {
    /* get board id byte info */
      board_id &= STATIC_ID_MASK; /* clear out extra bits */
      temp7 = inb_p(ioaddr+0xe);
      /* check for soft config bit */
      if ((temp7 & 0x20) != 0) {
	if ((board_id == WD8003EB) || (board_id == WD8003W))
	  board_id |= ALTERNATE_IRQ_BIT;
      }
    }
    if (board_rev >= 3) {
      board_id &= BID_EEPROM_OVERRIDE;
      board_id |= INTERFACE_584_CHIP;
      /* get eeprom info */
      /* first recall the reserved engineering bytes from eeprom */
      outb_p((inb_p(ioaddr+1) & 0xc) | 2,ioaddr+1); /* set other bit */
      outb_p((inb_p(ioaddr+3) & 0xf) | 0xa0,ioaddr+3); /* set engr page */
      outb_p((inb_p(ioaddr+1) & 0xc) | 0x12,ioaddr+1); /* set rla, other bit */
      while (inb_p(ioaddr+1) & 0x10); /* wait for recall */
      if ((inb_p(ioaddr+9) & 0x18) == 8) {
	board_id |= BOARD_16BIT;
	/* is 16 bit board in 16 bit slot? */
	if ((inb_p(ioaddr+1) & 1) == 1) board_id |= SLOT_16BIT;
      }
      temp7 = inb_p(ioaddr+8);
      switch(temp7 & 7) {
      case 0: board_id |= STARLAN_MEDIA;
	break;
      case 2: board_id |= TWISTED_PAIR_MEDIA;
	break;
      case 3: board_id |= EW_MEDIA;
	break;
      default: board_id |= ETHERNET_MEDIA;
	break;
      }
      if ((temp7 & 0x18) == 8) board_id |= ALTERNATE_IRQ_BIT;
      switch(temp7 & 0xe0) {
      case 0x40: board_id |= RAM_SIZE_8K;
	break;
      case 0x60: if ((board_id & BOARD_16BIT) && !(board_id & SLOT_16BIT))
	  board_id |= RAM_SIZE_8K;
	else board_id |= RAM_SIZE_16K;
	break;
      case 0x80: board_id |= RAM_SIZE_32K;
	break;
      case 0xa0: if ((board_id & BOARD_16BIT) && !(board_id & SLOT_16BIT))
	  board_id |= RAM_SIZE_32K;
	else board_id |= RAM_SIZE_64K;
	break;
      default: board_id |= RAM_SIZE_UNKNOWN;
	break;
      }
      /* now recall the lan address from eeprom */
      outb_p((inb_p(ioaddr+1) & 0xc) | 2,ioaddr+1); /* set other bit */
      outb_p((inb_p(ioaddr+3) & 0xf) | 0x80,ioaddr+3); /* set page */
      outb_p((inb_p(ioaddr+1) & 0xc) | 0x10,ioaddr+1); /* set rla */
      while (inb_p(ioaddr+1) & 0x10); /* wait for recall */
      if (is_nic_690(ioaddr)) board_id |= NIC_690_BIT; /* check for 690 */
      return(board_id);
    }
    /* get ram size */
    if (board_rev >= 2) {
      temp1 = inb_p(ioaddr+0xe); /* get hardware id byte */
      temp2 = board_id & STATIC_ID_MASK; /* get board type */
      if ((temp2 == WD8003E) || (temp2 == WD8003S) ||
	  (temp2 == WD8003WT) || (temp2 == WD8003W) ||
	  (temp2 == WD8003EB)) {
	/* hardware ram size bit determines 8K or 32K */
	board_id |= (temp1 & 0x40) ? RAM_SIZE_32K : RAM_SIZE_8K;
      }
      else {
	if (temp2 == WD8013EBT) {
	  if (board_id & SLOT_16BIT) {
	    /* hardware ram size bit determines 16K or 64K */
	    board_id |= (temp1 & 0x40) ? RAM_SIZE_64K : RAM_SIZE_16K;
	  }
	  else {
	      /* hardware ram size bit determines 8K or 32K */
	      board_id |= (temp1 & 0x40) ? RAM_SIZE_32K : RAM_SIZE_8K;
	    }
	}
	else board_id |= RAM_SIZE_UNKNOWN;
      }
    }
    else {
      /* old rev boards */
      if (board_id & BOARD_16BIT) {
	if (board_id & SLOT_16BIT) board_id |= RAM_SIZE_16K;
	else board_id |= RAM_SIZE_8K;
      }
      else {
	if (board_id & INTERFACE_CHIP) {
	  /* look at memory size bit in register 1 */
	  board_id |= (inb_p(ioaddr+1) & 8) ? RAM_SIZE_32K : RAM_SIZE_8K;
	}
	/* can't determine ram size */
	else board_id |= RAM_SIZE_UNKNOWN;
      }
    }
    if (is_nic_690(ioaddr)) board_id |= NIC_690_BIT; /* check for 690 */
  }
 return(board_id);
}


/*
   get lots of configuration information about any western digital
   or SMC ethernet card.

   gets the following info:
   board id
   media type - starlan or ethernet or twisted pair
   interface chip type - none or 583 or 584 or 593 or 594
   NIC chip type - 8390 or 690
   8/16 bit board
   8/16 bit slot
   base address of ram
   size of ram
   interrupt line used
   base address of rom
   size of rom

   also returns the following values:
   0 = all is wonderful
   1 = no interface chip, so could not do much
   -1 = no board found
*/

static unsigned char irqlist[8] = {9,3,5,7,10,11,15,4};

int smc_get_config(CNFG_Adapter *cfg_info)
{
  unsigned long board_id,temp1;
  unsigned char csum;
  unsigned short base_addr;
  int irnum,i,rbase1;
  /* check if board is there via checksum */
  csum = 0;
  base_addr = cfg_info->base_io;
  for (i = 0; i< 8; i++) csum += inb_p(base_addr + 8 + i);
  if (csum != 0xff) return(-1); /* if bad checksum report no board */
  board_id = smc_get_board_id(base_addr);
  cfg_info->bid = board_id & STATIC_ID_MASK;
  cfg_info->full_bid = board_id;
  /* copy ram size from board_id to the config structure */
  temp1 = board_id & RAM_SIZE_MASK; /* get ram size bits */
  if ((temp1 >= RAM_SIZE_8K) && (temp1 <= RAM_SIZE_64K)) {
    cfg_info->ram_size = 8 << ((temp1 >> 16) - 2);
  }
  if (!(board_id & INTERFACE_CHIP)) return(1); /* no interface chip */
  /* get interrupt line */
  irnum = 0;
  if ((board_id & INTERFACE_CHIP_MASK) != INTERFACE_5X3_CHIP) {
    irnum = inb_p(base_addr + 1) & 4;
  }
  irnum += (inb_p(base_addr + 4) & 0x60) >> 5;
  if ((irnum == 2) && !(board_id & ALTERNATE_IRQ_BIT))
    cfg_info->irq_line = 4;
  else cfg_info->irq_line = irqlist[irnum];
  /* get irq status */
  /* get ram base */
  rbase1 = inb_p(base_addr) & 0x3f;
  if ((board_id & INTERFACE_CHIP_MASK) != INTERFACE_5X3_CHIP) {
    cfg_info->ram_base =
      (inb_p(base_addr + 5) & 0x1f) << 19 | (rbase1 << 13);
  }
  else cfg_info->ram_base = (rbase1 | 0x40) << 13;
  /* get rom base */
  /* get rom size */
  /* get boot status */
  /* get zero wait state */
  /* get name of board type */
 switch(cfg_info->bid) {
 case WD8003W: strcpy(cfg_info->name,"WD8003W");
   break;
 case WD8003E: strcpy(cfg_info->name,"WD8003E");
   break;
 case WD8003S: strcpy(cfg_info->name,"WD8003S");
   break;
 case WD8003WT: strcpy(cfg_info->name,"WD8003WT");
   break;
 case WD8003EB:
   if ((cfg_info->full_bid & INTERFACE_CHIP_MASK) == INTERFACE_584_CHIP)
     strcpy(cfg_info->name,"WD8003EP");
   else
     strcpy(cfg_info->name,"WD8003EB");
   break;
 case WD8003EW: strcpy(cfg_info->name,"WD8003EW");
   break;
 case WD8013EBT: strcpy(cfg_info->name,"WD8013EBT");
   break;
 case WD8013EB:
   if ((cfg_info->full_bid & INTERFACE_CHIP_MASK) == INTERFACE_584_CHIP)
     strcpy(cfg_info->name,"WD8013EP");
   else
     strcpy(cfg_info->name,"WD8013EB");
   break;
   break;
 case WD8013W: strcpy(cfg_info->name,"WD8013W");
   break;
 case WD8013EW: strcpy(cfg_info->name,"WD8013EW");
   break;
 default: sprintf(cfg_info->name,"%x\0",cfg_info->bid);
   break;
 }

 return(0);
}


