[Prev][Next][Index][Thread]

CS8900 ethernet driver



Here is a somewhat debugged ethernet driver for the CS89x0 ethernet chip
from crystal semiconductor.  This is the chip used in the DIMMPC
(http://wearables.stanford.edu).
We haven't done extensive testing but it does run the pingreply kernel and
netboot.

If you make any improvements let us know!!

Enjoy and Deploy!!


							Geoffw
							Ashtar Labs
/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
/*
	Written 1996 by Russell Nelson, with reference to skeleton.c
	written 1993-1994 by Donald Becker.

	This software may be used and distributed according to the terms
	of the GNU Public License, incorporated herein by reference.

	The author may be reached at nelson@crynwr.com, Crynwr
	Software, 521 Pleasant Valley Rd., Potsdam, NY 13676

  Changelog:

  Mike Cruse        : mcruse@cti-ltd.com
                    : Changes for Linux 2.0 compatibility. 
                    : Added dev_id parameter in net_interrupt(),
                    : request_irq() and free_irq(). Just NULL for now.

  Mike Cruse        : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
                    : in net_open() and net_close() so kerneld would know
                    : that the module is in use and wouldn't eject the 
                    : driver prematurely.

  Mike Cruse        : Rewrote init_module() and cleanup_module using 8390.c
                    : as an example. Disabled autoprobing in init_module(),
                    : not a good thing to do to other devices while Linux
                    : is running from all accounts.

  Russ Nelson : Jul 13 1998.  Added RxOnly DMA support.

  Ashtar Labs	  : Modified to run on the Ethernet Card of the JumpTec DIMMPC
                    : under OSKit 4/12/99

*/

static char *version =
"cs89x0.c:v1.03 12/27/96 Russell Nelson <nelson@crynwr.com>";

/* ======================= configure the driver here ======================= */

/* we can do some optimizations that *require* that the Packet Page be
   mapped into memory, but you have to change the following line to
   get them.  We automatically use memory for bulk transfer if the
   Packet Page is found to have a memory map. */
#define REQUIRE_MEMORY 0

/* we can use a DMA buffer to receiving packets.  It takes more memory. */
#define ALLOW_DMA 1

/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 2
#endif

/* ======================= end of configuration ======================= */


/* Always include 'config.h' first in case the user wants to turn on
   or override something. */
#include <linux/config.h>
#ifdef MODULE
#include <linux/module.h>
#include <linux/version.h>
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif

#define PRINTK(x) printk x

/*
  Sources:

	Crynwr packet driver epktisa.

	Crystal Semiconductor data sheets.

*/

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/delay.h>

#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/errno.h>

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "cs89x0.h"

/* First, a few definitions that the brave might change. */
/* A zero-terminated list of I/O addresses to be probed. */
static unsigned int netcard_portlist[] =
   { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};

static unsigned int net_debug = NET_DEBUG;
static int track_intr = 0;
/* The number of low I/O ports used by the ethercard. */
#define NETCARD_IO_EXTENT	16

/* we allow the user to override various values normally set in the EEPROM */
#define FORCE_RJ45	0x01    /* pick one of these three */
#define FORCE_AUI	0x02
#define FORCE_BNC	0x04
#define FORCE_AUTO	0x10    /* pick one of these three */
#define FORCE_HALF	0x20
#define FORCE_FULL	0x30

/* Information that need to be kept for each board. */
struct net_local {
	struct enet_statistics stats;
	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
	char chip_revision;	/* revision letter of the chip ('A'...) */
	int send_cmd;		/* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
	int auto_neg_cnf;	/* auto-negotiation word from EEPROM */
	int adapter_cnf;	/* adapter configuration from EEPROM */
	int isa_config;		/* ISA configuration from EEPROM */
	int irq_map;		/* IRQ map from EEPROM */
	int rx_mode;		/* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
	int curr_rx_cfg;	/* a copy of PP_RxCFG */
	int linectl;		/* either 0 or LOW_RX_SQUELCH, depending on configuration. */
	int send_underrun;	/* keep track of how many underruns in a row we get */
	int force;		/* force various values; see FORCE* above. */
#if ALLOW_DMA
	int dma;		/* DMA channel */
	int dmasize;		/* 16 or 64 */
	unsigned char *dma_buff;	/* points to the beginning of the buffer */
	unsigned char *end_dma_buff;	/* points to the end of the buffer */
	unsigned char *rx_dma_ptr;	/* points to the next packet  */
#endif
};

/* Index to functions, as function prototypes. */

extern int cs89x0_probe(struct device *dev);

static int cs89x0_probe1(struct device *dev, int ioaddr);
static int net_open(struct device *dev);
static int	net_send_packet(struct sk_buff *skb, struct device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void set_multicast_list(struct device *dev);
static void net_rx(struct device *dev);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
static void reset_chip(struct device *dev);
static int get_eeprom_data(struct device *dev, int off, int len, int *buffer);
static int get_eeprom_cksum(int off, int len, int *buffer);
static int set_mac_address(struct device *dev, void *addr);
static void get_dma_channel(struct device *dev);
static void count_rx_errors(int status, struct net_local *lp);


/* Example routines you must write ;->. */
#define tx_done(dev) 1



/* Check for a network adaptor of this type, and return '0' iff one exists.
   If dev->base_addr == 0, probe all likely locations.
   If dev->base_addr == 1, always return failure.
   If dev->base_addr == 2, allocate space for the device and return success
   (detachable devices only).
   */
#ifdef HAVE_DEVLIST
/* Support for a alternate probe manager, which will eliminate the
   boilerplate below. */
struct netdev_entry netcard_drv =
{"netcard", cs89x0_probe1, NETCARD_IO_EXTENT, netcard_portlist};
#else
int
cs89x0_probe(struct device *dev)
{
	int i;
	int base_addr = dev ? dev->base_addr : 0;
	base_addr = 0x320;
	if (base_addr > 0x1ff)		/* Check a single specified location. */
		return cs89x0_probe1(dev, base_addr);
	else if (base_addr != 0)	/* Don't probe at all. */
		return ENXIO;

	for (i = 0; netcard_portlist[i]; i++) {
		int ioaddr = netcard_portlist[i];
		if (check_region(ioaddr, NETCARD_IO_EXTENT))
			continue;
		if (cs89x0_probe1(dev, ioaddr) == 0)
			return 0;
	}
	printk(KERN_NOTICE "cs89x0: no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP\n");
	return ENODEV;
}
#endif

int inline
readreg_io(struct device *dev, int portno)
{
	outw(portno, dev->base_addr + ADD_PORT);
	return inw(dev->base_addr + DATA_PORT);
}

void inline
writereg_io(struct device *dev, int portno, int value)
{
	outw(portno, dev->base_addr + ADD_PORT);
	outw(value, dev->base_addr + DATA_PORT);
}

int inline
readreg(struct device *dev, int portno)
{
#if REQUIRE_MEMORY
	return readw(dev->mem_start + portno);
#else
	outw(portno, dev->base_addr + ADD_PORT);
	return inw(dev->base_addr + DATA_PORT);
#endif
}

void inline
writereg(struct device *dev, int portno, int value)
{
#if REQUIRE_MEMORY
	writew(dev->mem_start + portno, value);
#else
	outw(portno, dev->base_addr + ADD_PORT);
	outw(value, dev->base_addr + DATA_PORT);
#endif
}

int inline
readword(struct device *dev, int portno)
{
#if REQUIRE_MEMORY
	return readw(dev->mem_start + portno);
#else
	return inw(dev->base_addr + portno);
#endif
}

void inline
writeword(struct device *dev, int portno, int value)
{
#if REQUIRE_MEMORY
	writew(dev->mem_start + portno, value);
#else
	outw(value, dev->base_addr + portno);
#endif
}

int
wait_eeprom_ready(struct device *dev)
{
	int timeout = jiffies;
	/* check to see if the EEPROM is ready, a timeout is used -
	   just in case EEPROM is ready when SI_BUSY in the
	   PP_SelfST is clear */
	while(readreg_io(dev, PP_SelfST) & SI_BUSY)
		if (jiffies - timeout >= 40)
			return -1;
	return 0;
}

int
get_eeprom_data(struct device *dev, int off, int len, int *buffer)
{
	int i;

	if (net_debug > 3) printk(KERN_DEBUG "EEPROM data from %x for %x:\n",off,len);
	for (i = 0; i < len; i++) {
		if (wait_eeprom_ready(dev) < 0) return -1;
		/* Now send the EEPROM read command and EEPROM location to read */
		writereg_io(dev, PP_EECMD, (off + i) | EEPROM_READ_CMD);
		if (wait_eeprom_ready(dev) < 0) return -1;
		buffer[i] = readreg_io(dev, PP_EEData);
		if (net_debug > 3) printk(KERN_DEBUG "%04x ", buffer[i]);
	}
	if (net_debug > 3) printk(KERN_DEBUG "\n");
        return 0;
}

int
get_eeprom_cksum(int off, int len, int *buffer)
{
	int i, cksum;

	cksum = 0;
	for (i = 0; i < len; i++)
		cksum += buffer[i];
	cksum &= 0xffff;
	if (cksum == 0)
		return 0;
	return -1;
}

/* This is the real probe routine.  Linux has a history of friendly device
   probes on the ISA bus.  A good device probes avoids doing writes, and
   verifies that the correct device exists and functions.  */

static int cs89x0_probe1(struct device *dev, int ioaddr)
{
	struct net_local *lp;
	static unsigned version_printed = 0;
	int i;
	unsigned rev_type = 0;
	int eeprom_buff[CHKSUM_LEN];
	unsigned short foo ;
	/* Initialize the device structure. */
	if (dev->priv == NULL) {
		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
                memset(dev->priv, 0, sizeof(struct net_local));
        }
	lp = (struct net_local *)dev->priv;

	/* if they give us an odd I/O address, then do ONE write to
           the address port, to get it back to address zero, where we
           expect to find the EISA signature word. */
	if (ioaddr & 1) {
		ioaddr &= ~ 1;
		if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
			return ENODEV;
		outw(PP_ChipID, ioaddr + ADD_PORT);
	}
	foo = inw(ioaddr + DATA_PORT ) ;

	if (!(foo ==  CHIP_EISA_ID_SIG || foo == 0x17)) 
		return ENODEV;

	/* Fill in the 'dev' fields. */
	dev->base_addr = ioaddr;

	/* get the chip type */
	rev_type = readreg_io(dev, PRODUCT_ID_ADD);
	lp->chip_type = rev_type &~ REVISON_BITS;
	lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';

	/* Check the chip type and revision in order to set the correct send command
	CS8920 revision C and CS8900 revision F can use the faster send. */
	lp->send_cmd = TX_AFTER_381;
	if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
		lp->send_cmd = TX_NOW;
	if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
		lp->send_cmd = TX_NOW;

	if (net_debug  &&  version_printed++ == 0)
		printk(KERN_INFO "%s\n", version);

	printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#3lx",
	       dev->name,
	       lp->chip_type==CS8900?'0':'2',
	       lp->chip_type==CS8920M?"M":"",
	       lp->chip_revision,
	       dev->base_addr);

	reset_chip(dev);

	if ((readreg_io(dev, PP_SelfST) & EEPROM_PRESENT) == 0) {
		printk("\n" KERN_WARNING "cs89x0: No EEPROM, relying on command line....\n");
	} else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
		printk("\n" KERN_WARNING "cs89x0: EEPROM read failed, relying on command line.\n");
        } else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
                printk("\n" KERN_WARNING "cs89x0: EEPROM checksum bad, relying on command line\n");
        } else {
                lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
                lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
                lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2];
                dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
                /* eeprom_buff has 32-bit ints, so we can't just memcpy it */
                for (i = 0; i < ETH_ALEN/2; i++) {
                        dev->dev_addr[i*2] = eeprom_buff[i];
                        dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;
                }
        }

        /* allow them to force multiple transceivers.  If they force multiple, autosense */
        { int count = 0;
        if (lp->force & FORCE_RJ45) {lp->adapter_cnf |= A_CNF_10B_T; count++; }
	if (lp->force & FORCE_AUI)  {lp->adapter_cnf |= A_CNF_AUI; count++; }
	if (lp->force & FORCE_BNC)  {lp->adapter_cnf |= A_CNF_10B_2; count++; }
        if (count > 1)                   lp->adapter_cnf |= A_CNF_MEDIA_AUTO;
        else if (lp->force & FORCE_RJ45) lp->adapter_cnf |= A_CNF_MEDIA_10B_T;
	else if (lp->force & FORCE_AUI)  lp->adapter_cnf |= A_CNF_MEDIA_AUI;
	else if (lp->force & FORCE_BNC)  lp->adapter_cnf |= A_CNF_MEDIA_10B_2;
        }

        /* We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */

        /* We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */

        /* we don't set the Ethernet address on the command line.  Use
           ifconfig IFACE hw ether AABBCCDDEEFF */

        /* For lp->isa_config, we use int=.  We don't allow stream
           transfer from the command line */

	dev->mem_start = 0xd0000;
	if (dev->mem_start) {
		printk(" memory %5lx,", dev->mem_start);
		if (lp->chip_type == CS8900) {
			writereg_io(dev, PP_CS8900_ISAMemB, dev->mem_start);
			writereg_io(dev, PP_CS8900_ISAMemB+2, dev->mem_start >>16);
		} else {
			writereg_io(dev, PP_CS8920_ISAMemB, (dev->mem_start & 0xff00) | (dev->mem_start >> 16 & 0xff));
		}
		writereg_io(dev, PP_BusCTL, MEMORY_ON);
		if ((*(int *)(dev->mem_start) &0xffff) != 0x630e) {
			printk(" but that isn't our memory (%x)!", *(unsigned int *)(dev->mem_start));
			dev->mem_start = 0;
		}
	}
#if REQUIRE_MEMORY
	if (dev->mem_start == 0) {

		printk(" has no memory map -- not installed\n");
		return ENODEV;
	}
#endif

	printk(" media %s%s%s",
	       (lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"",
	       (lp->adapter_cnf & A_CNF_AUI)?"AUI,":"",
	       (lp->adapter_cnf & A_CNF_10B_2)?"BNC,":"");

	lp->irq_map = 0xffff;

	/* If this is a CS8900 then no pnp soft */
	if (lp->chip_type != CS8900 &&
	    /* Check if the ISA IRQ has been set  */
		(i = readreg(dev, PP_CS8920_ISAINT) & 0xff,
		 (i != 0 && i < CS8920_NO_INTS))) {
		if (!dev->irq)
			dev->irq = i;
	} else {
		i = lp->isa_config & INT_NO_MASK;
		if (lp->chip_type == CS8900) {
			/* the table that follows is dependent upon how you wired up your cs8900
			 * in your system.  The table is the same as the cs8900 engineering demo
			 * board.  irq_map also depends on the contents of the table.  Also see
			 * write_irq, which is the reverse mapping of the table below. */
			switch(i) {
			case 0: i = 10; break;
			case 1: i = 11; break;
			case 2: i = 12; break;
			case 3: i =  5; break;
			default: printk("\n" KERN_WARNING "cs89x0: bug: isa_config is %d\n", i);
			}
			lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */
		} else {
			int irq_map_buff[IRQ_MAP_LEN/2];

			if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA,
					    IRQ_MAP_LEN/2,
					    irq_map_buff) >= 0) {
				if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT)
					lp->irq_map = (irq_map_buff[0]>>8) | (irq_map_buff[1] << 8);
			}
		}
		if (!dev->irq)
			dev->irq = i;
	}

	printk(" IRQ %d,", dev->irq);

	get_dma_channel(dev);

	/* print the ethernet address. */
	for (i = 0; i < ETH_ALEN; i++)
		printk(" %2.2x", dev->dev_addr[i]);

	/* Grab the region so we can find another board if autoIRQ fails. */
	request_region(ioaddr, NETCARD_IO_EXTENT,"cs89x0");

	dev->open		= net_open;
	dev->stop		= net_close;
	dev->hard_start_xmit = net_send_packet;
	dev->get_stats	= net_get_stats;
	dev->set_multicast_list = &set_multicast_list;
	dev->set_mac_address = &set_mac_address;

	/* Fill in the fields of the device structure with ethernet values. */
	ether_setup(dev);

	printk("\n");
	return 0;
}

#if 0


void
print_registers(struct device *dev)
{
	int value;
	int ioaddr = dev->base_addr;

	printk("cs89x0: PP_RxCFG:");
	value = readreg(dev, PP_RxCFG);
	printk(" %sSKIP_1", value & SKIP_1?"":"~");
	printk(" %sRX_STREAM_ENBL", value & RX_STREAM_ENBL?"":"~");
	printk(" %sRX_OK_ENBL", value & RX_OK_ENBL?"":"~");
	printk(" %sRX_DMA_ONLY", value & RX_DMA_ONLY?"":"~");
	printk(" %sAUTO_RX_DMA", value & AUTO_RX_DMA?"":"~");
	printk(" %sBUFFER_CRC", value & BUFFER_CRC?"":"~");
	printk(" %sRX_CRC_ERROR_ENBL", value & RX_CRC_ERROR_ENBL?"":"~");
	printk(" %sRX_RUNT_ENBL", value & RX_RUNT_ENBL?"":"~");
	printk(" %sRX_EXTRA_DATA_ENBL", value & RX_EXTRA_DATA_ENBL?"":"~");
	printk("\n");

	printk("cs89x0: PP_RxCTL:");
	value = readreg(dev, PP_RxCTL);
	printk(" %sRX_IA_HASH_ACCEPT", value & RX_IA_HASH_ACCEPT?"":"~");
	printk(" %sRX_PROM_ACCEPT", value & RX_PROM_ACCEPT?"":"~");
	printk(" %sRX_OK_ACCEPT", value & RX_OK_ACCEPT?"":"~");
	printk(" %sRX_MULTCAST_ACCEPT", value & RX_MULTCAST_ACCEPT?"":"~");
	printk(" %sRX_IA_ACCEPT", value & RX_IA_ACCEPT?"":"~");
	printk(" %sRX_BROADCAST_ACCEPT", value & RX_BROADCAST_ACCEPT?"":"~");
	printk(" %sRX_BAD_CRC_ACCEPT", value & RX_BAD_CRC_ACCEPT?"":"~");
	printk(" %sRX_RUNT_ACCEPT", value & RX_RUNT_ACCEPT?"":"~");
	printk(" %sRX_EXTRA_DATA_ACCEPT", value & RX_EXTRA_DATA_ACCEPT?"":"~");
	printk("\n");

	printk("cs89x0: PP_TxCFG:");
	value = readreg(dev, PP_TxCFG);
	printk(" %sTX_LOST_CRS_ENBL", value & TX_LOST_CRS_ENBL?"":"~");
	printk(" %sTX_SQE_ERROR_ENBL", value & TX_SQE_ERROR_ENBL?"":"~");
	printk(" %sTX_OK_ENBL", value & TX_OK_ENBL?"":"~");
	printk(" %sTX_LATE_COL_ENBL", value & TX_LATE_COL_ENBL?"":"~");
	printk(" %sTX_JBR_ENBL", value & TX_JBR_ENBL?"":"~");
	printk(" %sTX_ANY_COL_ENBL", value & TX_ANY_COL_ENBL?"":"~");
	printk(" %sTX_16_COL_ENBL", value & TX_16_COL_ENBL?"":"~");
	printk("\n");

	printk("cs89x0: PP_TxCMD:");
	value = readreg(dev, PP_TxCMD);
	switch(value & TX_START_ALL_BYTES) {
	case TX_START_4_BYTES:	printk(" TX_START_4_BYTES"); break;
	case TX_START_64_BYTES:	printk(" TX_START_64_BYTES"); break;
	case TX_START_128_BYTES:	printk(" TX_START_128_BYTES"); break;
	case TX_START_ALL_BYTES:	printk(" TX_START_ALL_BYTES"); break;
	}
	printk(" %sTX_FORCE", value & TX_FORCE?"":"~");
	printk(" %sTX_ONE_COL", value & TX_ONE_COL?"":"~");
	printk(" %sTX_TWO_PART_DEFF_DISABLE", value & TX_TWO_PART_DEFF_DISABLE?"":"~");
	printk(" %sTX_NO_CRC", value & TX_NO_CRC?"":"~");
	printk(" %sTX_RUNT", value & TX_RUNT?"":"~");
	printk("\n");

	printk("cs89x0: PP_BufCFG:");
	value = readreg(dev, PP_BufCFG);
	printk(" %sGENERATE_SW_INTERRUPT", value & GENERATE_SW_INTERRUPT?"":"~");
	printk(" %sRX_DMA_ENBL", value & RX_DMA_ENBL?"":"~");
	printk(" %sREADY_FOR_TX_ENBL", value & READY_FOR_TX_ENBL?"":"~");
	printk(" %sTX_UNDERRUN_ENBL", value & TX_UNDERRUN_ENBL?"":"~");
	printk(" %sRX_MISS_ENBL", value & RX_MISS_ENBL?"":"~");
	printk(" %sRX_128_BYTE_ENBL", value & RX_128_BYTE_ENBL?"":"~");
	printk(" %sTX_COL_COUNT_OVRFLOW_ENBL", value & TX_COL_COUNT_OVRFLOW_ENBL?"":"~");
	printk(" %sRX_MISS_COUNT_OVRFLOW_ENBL", value & RX_MISS_COUNT_OVRFLOW_ENBL?"":"~");
	printk(" %sRX_DEST_MATCH_ENBL", value & RX_DEST_MATCH_ENBL?"":"~");
	printk("\n");

	printk("cs89x0: PP_LineCTL:");
	value = readreg(dev, PP_LineCTL);
	printk(" %sSERIAL_RX_ON", value & SERIAL_RX_ON?"":"~");
	printk(" %sSERIAL_TX_ON", value & SERIAL_TX_ON?"":"~");
	printk(" %sAUI_ONLY", value & AUI_ONLY?"":"~");
	printk(" %sAUTO_AUI_10BASET", value & AUTO_AUI_10BASET?"":"~");
	printk(" %sMODIFIED_BACKOFF", value & MODIFIED_BACKOFF?"":"~");
	printk(" %sNO_AUTO_POLARITY", value & NO_AUTO_POLARITY?"":"~");
	printk(" %sTWO_PART_DEFDIS", value & TWO_PART_DEFDIS?"":"~");
	printk(" %sLOW_RX_SQUELCH", value & LOW_RX_SQUELCH?"":"~");
	printk("\n");

	printk("cs89x0: PP_SelfCTL:");
	value = readreg(dev, PP_SelfCTL);
	printk(" %sPOWER_ON_RESET", value & POWER_ON_RESET?"":"~");
	printk(" %sSW_STOP", value & SW_STOP?"":"~");
	printk(" %sSLEEP_ON", value & SLEEP_ON?"":"~");
	printk(" %sAUTO_WAKEUP", value & AUTO_WAKEUP?"":"~");
	printk(" %sHCB0_ENBL", value & HCB0_ENBL?"":"~");
	printk(" %sHCB1_ENBL", value & HCB1_ENBL?"":"~");
	printk(" %sHCB0", value & HCB0?"":"~");
	printk(" %sHCB1", value & HCB1?"":"~");
	printk("\n");

	printk("cs89x0: PP_BusCTL:");
	value = readreg(dev, PP_BusCTL);
	printk(" %sRESET_RX_DMA", value & RESET_RX_DMA?"":"~");
	printk(" %sMEMORY_ON", value & MEMORY_ON?"":"~");
	printk(" %sDMA_BURST_MODE", value & DMA_BURST_MODE?"":"~");
	printk(" %sIO_CHANNEL_READY_ON", value & IO_CHANNEL_READY_ON?"":"~");
	printk(" %sRX_DMA_SIZE_64K", value & RX_DMA_SIZE_64K?"":"~");
	printk(" %sENABLE_IRQ", value & ENABLE_IRQ?"":"~");
	printk("\n");

	printk("cs89x0: PP_TestCTL:");
	value = readreg(dev, PP_TestCTL);
	printk(" %sLINK_OFF", value & LINK_OFF?"":"~");
	printk(" %sENDEC_LOOPBACK", value & ENDEC_LOOPBACK?"":"~");
	printk(" %sAUI_LOOPBACK", value & AUI_LOOPBACK?"":"~");
	printk(" %sBACKOFF_OFF", value & BACKOFF_OFF?"":"~");
	printk(" %sFAST_TEST", value & FAST_TEST?"":"~");
	printk("\n");

	printk("cs89x0: PP_RxEvent:");
	value = readreg(dev, PP_RxEvent);
	printk(" %sRX_IA_HASHED", value & RX_IA_HASHED?"":"~");
	printk(" %sRX_DRIBBLE", value & RX_DRIBBLE?"":"~");
	printk(" %sRX_OK", value & RX_OK?"":"~");
	printk(" %sRX_HASHED", value & RX_HASHED?"":"~");
	printk(" %sRX_IA", value & RX_IA?"":"~");
	printk(" %sRX_BROADCAST", value & RX_BROADCAST?"":"~");
	printk(" %sRX_CRC_ERROR", value & RX_CRC_ERROR?"":"~");
	printk(" %sRX_RUNT", value & RX_RUNT?"":"~");
	printk(" %sRX_EXTRA_DATA", value & RX_EXTRA_DATA?"":"~");
	printk("\n");

	printk("cs89x0: PP_TxEvent:");
	value = readreg(dev, PP_TxEvent);
	printk(" %sTX_LOST_CRS", value & TX_LOST_CRS?"":"~");
	printk(" %sTX_SQE_ERROR", value & TX_SQE_ERROR?"":"~");
	printk(" %sTX_OK", value & TX_OK?"":"~");
	printk(" %sTX_LATE_COL", value & TX_LATE_COL?"":"~");
	printk(" %sTX_JBR", value & TX_JBR?"":"~");
	printk(" %sTX_16_COL", value & TX_16_COL?"":"~");
	printk(" %sTX_SEND_OK_BITS", value & TX_SEND_OK_BITS?"":"~");
	printk(" %sTX_COL_COUNT_MASK", value & TX_COL_COUNT_MASK?"":"~");
	printk("\n");

	printk("cs89x0: PP_BufEvent:");
	value = readreg(dev, PP_BufEvent);
	printk(" %sSW_INTERRUPT", value & SW_INTERRUPT?"":"~");
	printk(" %sRX_DMA", value & RX_DMA?"":"~");
	printk(" %sREADY_FOR_TX", value & READY_FOR_TX?"":"~");
	printk(" %sTX_UNDERRUN", value & TX_UNDERRUN?"":"~");
	printk(" %sRX_MISS", value & RX_MISS?"":"~");
	printk(" %sRX_128_BYTE", value & RX_128_BYTE?"":"~");
	printk(" %sTX_COL_OVRFLW", value & TX_COL_OVRFLW?"":"~");
	printk(" %sRX_MISS_OVRFLW", value & RX_MISS_OVRFLW?"":"~");
	printk(" %sRX_DEST_MATCH", value & RX_DEST_MATCH?"":"~");
	printk("\n");

	printk("cs89x0: PP_LineST:");
	value = readreg(dev, PP_LineST);
	printk(" %sLINK_OK", value & LINK_OK?"":"~");
	printk(" %sAUI_ON", value & AUI_ON?"":"~");
	printk(" %sTENBASET_ON", value & TENBASET_ON?"":"~");
	printk(" %sPOLARITY_OK", value & POLARITY_OK?"":"~");
	printk(" %sCRS_OK", value & CRS_OK?"":"~");
	printk("\n");

	printk("cs89x0: PP_SelfST:");
	value = readreg(dev, PP_SelfST);
	printk(" %sACTIVE_33V", value & ACTIVE_33V?"":"~");
	printk(" %sINIT_DONE", value & INIT_DONE?"":"~");
	printk(" %sSI_BUSY", value & SI_BUSY?"":"~");
	printk(" %sEEPROM_PRESENT", value & EEPROM_PRESENT?"":"~");
	printk(" %sEEPROM_OK", value & EEPROM_OK?"":"~");
	printk(" %sEL_PRESENT", value & EL_PRESENT?"":"~");
	printk(" %sEE_SIZE_64", value & EE_SIZE_64?"":"~");
	printk("\n");

	printk("cs89x0: PP_BusST:");
	value = readreg(dev, PP_BusST);
	printk(" %sTX_BID_ERROR", value & TX_BID_ERROR?"":"~");
	printk(" %sREADY_FOR_TX_NOW", value & READY_FOR_TX_NOW?"":"~");
	printk("\n");

	printk("cs89x0: PP_AutoNegCTL:");
	value = readreg(dev, PP_AutoNegCTL);
	printk(" %sRE_NEG_NOW", value & RE_NEG_NOW?"":"~");
	printk(" %sALLOW_FDX", value & ALLOW_FDX?"":"~");
	printk(" %sAUTO_NEG_ENABLE", value & AUTO_NEG_ENABLE?"":"~");
	printk(" %sNLP_ENABLE", value & NLP_ENABLE?"":"~");
	printk(" %sFORCE_FDX", value & FORCE_FDX?"":"~");
	printk(" %sAUTO_NEG_BITS", value & AUTO_NEG_BITS?"":"~");
	printk(" %sAUTO_NEG_MASK", value & AUTO_NEG_MASK?"":"~");
	printk("\n");

	printk("cs89x0: PP_AutoNegST:");
	value = readreg(dev, PP_AutoNegST);
	printk(" %sAUTO_NEG_BUSY", value & AUTO_NEG_BUSY?"":"~");
	printk(" %sFLP_LINK", value & FLP_LINK?"":"~");
	printk(" %sFLP_LINK_GOOD", value & FLP_LINK_GOOD?"":"~");
	printk(" %sLINK_FAULT", value & LINK_FAULT?"":"~");
	printk(" %sHDX_ACTIVE", value & HDX_ACTIVE?"":"~");
	printk(" %sFDX_ACTIVE", value & FDX_ACTIVE?"":"~");
	printk("\n");

	if (0) { int i;
	for (i = 0x158; i < 0x15e; i++) {
		outw(i, ioaddr + ADD_PORT);
		if ((i & 0x7) == 0)
			printk("%04x:", i);
		printk(" %04x", inw(ioaddr + DATA_PORT));
		if ((i & 0x7) == 0x7)
			printk("\n");
	}
	}

#if ALLOW_DMA
	if (dev->dma) { int i;
	struct net_local *lp = (struct net_local *)dev->priv;

	for (i = 0; i < 0x100; i++) {
		if ((i & 0xf) == 0)
			printk("%04x:", i);
		printk(" %02x", lp->dma_buff[i]);
		if ((i & 0xf) == 0xf)
			printk("\n");
	}
	}
	printk("\n");
#endif
}

void
print_packet_page(int ioaddr)
{
	int i;

	for (i = 0x100; i < 0x140; i++) {
		outw(i, ioaddr + ADD_PORT);
		if ((i & 0x7) == 0)
			printk("%04x:", i);
		printk(" %04x", inw(ioaddr + DATA_PORT));
		if ((i & 0x7) == 0x7)
			printk("\n");
	}
}
#endif


/*******************************************************
 * This page contains DMA routines that are either enabled or disabled
 * by ALLOW_DMA.
 *******************************************************/

#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)

void
get_dma_channel(struct device *dev)
{
#if ALLOW_DMA
	struct net_local *lp = (struct net_local *)dev->priv;

	if (lp->dma) {
		dev->dma = lp->dma;
		lp->isa_config |= ISA_RxDMA;
	} else {
		if ((lp->isa_config & ANY_ISA_DMA) == 0)
			return;
		dev->dma = lp->isa_config & DMA_NO_MASK;
		if (lp->chip_type == CS8900)
			dev->dma += 5;
		if (dev->dma < 5 || dev->dma > 7) {
			lp->isa_config &= ~ANY_ISA_DMA;
			return;
		}
	}
	printk(" DMA %d,", dev->dma);
	return;
#endif
}

void
write_dma(struct device *dev, int chip_type, int dma)
{
	struct net_local *lp = (struct net_local *)dev->priv;

#if ALLOW_DMA
	if ((lp->isa_config & ANY_ISA_DMA) == 0)
		return;
	if (chip_type == CS8900) {
		writereg(dev, PP_CS8900_ISADMA, dma-5);
	} else {
		writereg(dev, PP_CS8920_ISADMA, dma);
	}
#endif
}

void
set_dma_cfg(struct device *dev)
{
#if ALLOW_DMA
	struct net_local *lp = (struct net_local *)dev->priv;

	if ((lp->isa_config & ANY_ISA_DMA) == 0)
		return;
	if (lp->isa_config & ISA_RxDMA)
		lp->curr_rx_cfg |= RX_DMA_ONLY;
	else
		lp->curr_rx_cfg |= AUTO_RX_DMA;	/* not that we support it... */
#endif
}

int
dma_bufcfg(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;

#if ALLOW_DMA
	return (lp->isa_config & ANY_ISA_DMA)? RX_DMA_ENBL : 0;
#else
	return 0;
#endif
}



int inline
dma_busctl(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int retval = 0;

#if ALLOW_DMA
	if (lp->isa_config & ANY_ISA_DMA)
		retval |= RESET_RX_DMA; /* Reset the DMA pointer */
	if (lp->isa_config & DMA_BURST)
		retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
	if (lp->dmasize == 64)
		retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
	retval |= MEMORY_ON;	/* we need memory enabled to use DMA. */
#endif
	return retval;
}

#if ALLOW_DMA
void
dma_rx(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	struct sk_buff *skb;
	int status, length;
	unsigned char *bp = lp->rx_dma_ptr;

	status = bp[0] + (bp[1]<<8);
	length = bp[2] + (bp[3]<<8);
	bp += 4;
	if (net_debug > 5)printk(KERN_NOTICE "%s: receiving packet at %lx, status %x, length %x\n", dev->name, (unsigned long)bp, status, length);
	if ((status & RX_OK) == 0) {
		count_rx_errors(status, lp);
		bp += (length + 3) & ~3;
		if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
		lp->rx_dma_ptr = bp;
		return;
	}

	/* Malloc up new buffer. */
	skb = alloc_skb(length, GFP_ATOMIC);
	if (skb == NULL) {
		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
		lp->stats.rx_dropped++;
		return;
	}
	skb->len = length;
	skb->dev = dev;

	if (bp + length > lp->end_dma_buff) {
		int semi_cnt = lp->end_dma_buff - bp;
		memcpy(skb_put(skb,semi_cnt), bp, semi_cnt);
		memcpy(skb_put(skb,length - semi_cnt), lp->dma_buff,
		       length - semi_cnt);
		bp = lp->dma_buff + length - semi_cnt;
	} else {
		memcpy(skb_put(skb,length), bp, length);
		bp += length;
	}
	if ((int)bp & 1) bp++;
	if ((int)bp & 2) bp+=2;
	/* should never be greater than, but might be equal to */
	if (bp >= lp->end_dma_buff) bp = lp->dma_buff;
	lp->rx_dma_ptr = bp;

	if (net_debug > 3)printk(KERN_DEBUG "%s: received %d byte packet of type %x\n",
                                 dev->name, length,
                                 (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
        skb->protocol=eth_type_trans(skb,dev);
	netif_rx(skb);
	lp->stats.rx_packets++;
	return;
}
#endif



void
reset_chip(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int ioaddr = dev->base_addr;
	int reset_start_time;
	writereg_io(dev, PP_SelfCTL, readreg_io(dev, PP_SelfCTL) | POWER_ON_RESET);

	/* wait 30 ms */
	udelay(1000*30);

	if (lp->chip_type != CS8900) {
		/* Hardware problem requires PNP registers to be reconfigured after a reset */
		outw(PP_CS8920_ISAINT, ioaddr + ADD_PORT);
		outb(dev->irq, ioaddr + DATA_PORT);
		outb(0,      ioaddr + DATA_PORT + 1);

		outw(PP_CS8920_ISAMemB, ioaddr + ADD_PORT);
		outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT);
		outb((dev->mem_start >> 24) & 0xff,   ioaddr + DATA_PORT + 1);
	}
	/* Wait until the chip is reset */
	reset_start_time = jiffies;
	while( (readreg_io(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2)
		;

}



void
control_dc_dc(struct device *dev, int on_not_off)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	unsigned int selfcontrol;
	int timenow = jiffies;
	/* control the DC to DC convertor in the SelfControl register.  */

	selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
	if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
		selfcontrol |= HCB1;
	else
		selfcontrol &= ~HCB1;
	writereg(dev, PP_SelfCTL, selfcontrol);

	/* Wait for the DC/DC converter to power up - 500ms */
	while (jiffies - timenow < 100)
		;

}

#define DETECTED_NONE  0
#define DETECTED_RJ45H 1
#define DETECTED_RJ45F 2
#define DETECTED_AUI   3
#define DETECTED_BNC   4

static int
detect_tp(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int timenow = jiffies;
        int fdx;

	if (net_debug > 1) printk(KERN_DEBUG "%s: Attempting TP\n", dev->name);

        /* If connected to another full duplex capable 10-Base-T card the link pulses
           seem to be lost when the auto detect bit in the LineCTL is set.
           To overcome this the auto detect bit will be cleared whilst testing the
           10-Base-T interface.  This would not be necessary for the sparrow chip but
           is simpler to do it anyway. */
	writereg(dev, PP_LineCTL, lp->linectl &~ AUI_ONLY);
	control_dc_dc(dev, 0);

        /* Delay for the hardware to work out if the TP cable is present - 150ms */

	for (timenow = jiffies; jiffies - timenow < 15; )
                ;
	if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
		return DETECTED_NONE;

	if (lp->chip_type == CS8900) {
                switch (lp->force & 0xf0) {
                case FORCE_AUTO: printk(KERN_WARNING "%s: cs8900 doesn't autonegotiate\n",dev->name);
                        return DETECTED_NONE;
                case FORCE_HALF: break;
                case FORCE_FULL: writereg(dev, PP_TestCTL, readreg(dev, PP_TestCTL) | FDX_8900); break;
                }
		fdx = readreg(dev, PP_TestCTL) & FDX_8900;
	} else {

                switch (lp->force & 0xf0) {
                case FORCE_AUTO: lp->auto_neg_cnf = AUTO_NEG_ENABLE; break;
                case FORCE_HALF: lp->auto_neg_cnf = 0; break;
                case FORCE_FULL: lp->auto_neg_cnf = RE_NEG_NOW| ALLOW_FDX; break;
                }
		writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);

		if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
			printk(KERN_INFO "%s: negotiating duplex...\n",dev->name);
			while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
				if (jiffies - timenow > 4000) {
					printk(KERN_ERR "Full / half duplex auto-negotiation timed out\n");
					break;
				}
			}
		}
		fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
        }
        if (fdx)
                return DETECTED_RJ45F;
        else
                return DETECTED_RJ45H;
}

/* send a test packet - return true if carrier bits are ok */
int
send_test_pkt(struct device *dev)
{
	int ioaddr = dev->base_addr;
	char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
				 0, 46, /* A 46 in network order */
				 0, 0, /* DSAP=0 & SSAP=0 fields */
				 0xf3, 0 /* Control (Test Req + P bit set) */ };
	long timenow = jiffies;

	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_TX_ON);

	memcpy(test_packet,          dev->dev_addr, ETH_ALEN);
	memcpy(test_packet+ETH_ALEN, dev->dev_addr, ETH_ALEN);

        writeword(dev, TX_CMD_PORT, TX_AFTER_ALL);
        writeword(dev, TX_LEN_PORT, ETH_ZLEN);

	/* Test to see if the chip has allocated memory for the packet */
	while (jiffies - timenow < 5)
		if (readreg(dev, PP_BusST) & READY_FOR_TX_NOW)
			break;
	if (jiffies - timenow >= 5)
		return 0;	/* this shouldn't happen */

	/* Write the contents of the packet */
	if (dev->mem_start) {
		memcpy((void *)dev->mem_start + PP_TxFrame, test_packet, ETH_ZLEN);
	} else {
		outsw(ioaddr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
	}

	if (net_debug > 1) printk(KERN_DEBUG "Sending test packet ");
	/* wait a couple of jiffies for packet to be received */
	for (timenow = jiffies; jiffies - timenow < 3; )
                ;
        if ((readreg(dev, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
                if (net_debug > 1) printk("succeeded\n");
                return 1;
        }
	if (net_debug > 1) printk("failed\n");
	return 0;
}


int
detect_aui(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;

	if (net_debug > 1) printk(KERN_DEBUG "%s: Attempting AUI\n", dev->name);
	control_dc_dc(dev, 0);

	writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);

	if (send_test_pkt(dev))
		return DETECTED_AUI;
	else
		return DETECTED_NONE;
}

int
detect_bnc(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;

	if (net_debug > 1) printk(KERN_DEBUG "%s: Attempting BNC\n", dev->name);
	control_dc_dc(dev, 1);

	writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);

	if (send_test_pkt(dev))
		return DETECTED_BNC;
	else
		return DETECTED_NONE;
}



void
write_irq(struct device *dev, int chip_type, int irq)
{
	int i;

	if (chip_type == CS8900) {
		switch(irq) {
		case 10: i = 0; break;
		case 11: i = 1; break;
		case 12: i = 2; break;
		case 5: i =  3; break;
		default: i = 3; break;
		}
		writereg(dev, PP_CS8900_ISAINT, i);
	} else {
		writereg(dev, PP_CS8920_ISAINT, irq);
	}
}

/* Open/initialize the board.  This is called (in the current kernel)
   sometime after booting when the 'ifconfig' program is run.

   This routine should set everything up anew at each open, even
   registers that "should" only need to be set once at boot, so that
   there is non-reboot way to recover if something goes wrong.
   */
static int
net_open(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int result = 0;
	int i;

	if (dev->irq < 2) {
		printk(KERN_NOTICE "net open irq %d \n", dev->irq);
		/* Allow interrupts to be generated by the chip */
		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
		for (i = 2; i < CS8920_NO_INTS; i++) if ((1 << dev->irq) & lp->irq_map) {
			if (request_irq (i, NULL, 0, "bogus", NULL) != -EBUSY) {
				write_irq(dev, lp->chip_type, i);
				writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT);
				if (!request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", NULL))
					break;
			}
		}


		if (i >= CS8920_NO_INTS) {
			writereg(dev, PP_BusCTL, 0);	/* disable interrupts. */
			return -EAGAIN;
		}
	} else {
		if (((1 << dev->irq) & lp->irq_map) == 0) {
			printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
                               dev->name, dev->irq, lp->irq_map);
			return -EAGAIN;
		}
		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
		write_irq(dev, lp->chip_type, dev->irq);
		if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", NULL)) {
			return -EAGAIN;
		}
	}

	irq2dev_map[dev->irq] = dev;

#if ALLOW_DMA
	if (lp->isa_config & ANY_ISA_DMA) {
		unsigned long flags;
#if 0
		lp->dma_buff = kmalloc(dmasize*1024,GFP_KERNEL | GFP_DMA);
#else
		lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL | GFP_DMA, lp->dmasize==16?4:16);
#endif
		if (!lp->dma_buff) {
			printk(KERN_ERR "%s: cannot get %dK memory for DMA\n", dev->name, lp->dmasize);
			goto release_irq;
		}
		if (net_debug > 1) printk(KERN_NOTICE "%s: dma %lx %lx\n", dev->name, (unsigned long)lp->dma_buff, (unsigned long)virt_to_bus(lp->dma_buff));
		if ((unsigned long)virt_to_bus(lp->dma_buff) >= MAX_DMA_ADDRESS ||
		    !dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) {
			printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name);
			goto release_irq;
		}
		{ int i; for (i=0;i<lp->dmasize*1024;i++) { lp->dma_buff[i] = i; }}
		if (request_dma(dev->dma, "cs89x0")) {
			printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma);
			goto release_irq;
		}
		write_dma(dev, lp->chip_type, dev->dma);
		lp->rx_dma_ptr = lp->dma_buff;
		lp->end_dma_buff = lp->dma_buff + lp->dmasize*1024;
		save_flags(flags);
		cli();
		disable_dma(dev->dma);
		clear_dma_ff(dev->dma);
		set_dma_mode(dev->dma, 0x14); /* auto_init as well */
		set_dma_addr(dev->dma, virt_to_bus(lp->dma_buff));
		set_dma_count(dev->dma, lp->dmasize*1024);
		enable_dma(dev->dma);
		restore_flags(flags);
	}
#endif

	/* set the Ethernet address */
	for (i=0; i < ETH_ALEN/2; i++)
		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));

	/* while we're testing the interface, leave interrupts disabled */
	writereg(dev, PP_BusCTL, MEMORY_ON);

	/* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
	if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) && (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
                lp->linectl = LOW_RX_SQUELCH;
	else
                lp->linectl = 0;

        /* check to make sure that they have the "right" hardware available */
	switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
	case A_CNF_MEDIA_10B_T: result = lp->adapter_cnf & A_CNF_10B_T; break;
	case A_CNF_MEDIA_AUI:   result = lp->adapter_cnf & A_CNF_AUI; break;
	case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break;
        default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);
        }
        if (!result) {
                printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name);
        release_irq:
                writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
                free_irq(dev->irq, NULL);
                irq2dev_map[dev->irq] = 0;
		return -EAGAIN;
	}

        /* set the hardware to the configured choice */
	switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
	case A_CNF_MEDIA_10B_T:
                result = detect_tp(dev);
                if (result==DETECTED_NONE) {
                        printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name);
                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
                                result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
                }
		break;
	case A_CNF_MEDIA_AUI:
                result = detect_aui(dev);
                if (result==DETECTED_NONE) {
                        printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name);
                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
                                result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */
                }
		break;
	case A_CNF_MEDIA_10B_2:
                result = detect_bnc(dev);
                if (result==DETECTED_NONE) {
                        printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name);
                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
                                result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
                }
		break;
	case A_CNF_MEDIA_AUTO:
		writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
		if (lp->adapter_cnf & A_CNF_10B_T)
			if ((result = detect_tp(dev)) != DETECTED_NONE)
				break;
		if (lp->adapter_cnf & A_CNF_AUI)
			if ((result = detect_aui(dev)) != DETECTED_NONE)
				break;
		if (lp->adapter_cnf & A_CNF_10B_2)
			if ((result = detect_bnc(dev)) != DETECTED_NONE)
				break;
		printk(KERN_ERR "%s: no media detected\n", dev->name);
                goto release_irq;
	}
	switch(result) {
	case DETECTED_NONE: printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name);
                goto release_irq;
	case DETECTED_RJ45H: printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);break;
	case DETECTED_RJ45F: printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);break;
	case DETECTED_AUI:   printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name);break;
	case DETECTED_BNC:   printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name);break;
	}

	/* Turn on both receive and transmit operations */
	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);

	/* Receive only error free packets addressed to this card */
	lp->rx_mode = 0;
	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);

	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
	if (lp->isa_config & STREAM_TRANSFER)
		lp->curr_rx_cfg |= RX_STREAM_ENBL;
	set_dma_cfg(dev);
	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);

	writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
	       TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);

	writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
                 dma_bufcfg(dev) |
		 TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);

	/* now that we've got our act together, enable everything */
	writereg(dev, PP_BusCTL, ENABLE_IRQ
		 | (dev->mem_start?MEMORY_ON : 0) /* turn memory on */
		 | dma_busctl(dev)
                 );
	dev->tbusy = 0;
	dev->interrupt = 0;
	dev->start = 1;
        MOD_INC_USE_COUNT;

	return 0;
}

static int
net_send_packet(struct sk_buff *skb, struct device *dev)
{
#ifdef NOTDEF
	print_registers(dev);
#endif
	if (dev->tbusy) {
		/* If we get here, some higher level has decided we are broken.
		   There should really be a "kick me" function call instead. */
		int tickssofar = jiffies - dev->trans_start;
		if (tickssofar < 5)
			return 1;
		if (net_debug > 0) printk(KERN_NOTICE "%s: transmit timed out, %s?\n", dev->name,
			   tx_done(dev) ? "IRQ conflict" : "network cable problem");
#ifdef NOTDEF
                print_registers(dev);
#endif
		/* Try to restart the adaptor. */
		dev->tbusy=0;
		dev->trans_start = jiffies;
	}

	/* If some higher layer thinks we've missed an tx-done interrupt
	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
	   itself. */
	if (skb == NULL) {
		dev_tint(dev);
		return 0;
	}

	/* Block a timer-based transmit from overlapping.  This could better be
	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
	if (set_bit(0, (void*)&dev->tbusy) != 0)
		printk(KERN_NOTICE "%s: Transmitter access conflict.\n", dev->name);
	else {
		struct net_local *lp = (struct net_local *)dev->priv;
		short ioaddr = dev->base_addr;
		unsigned long flags;

		if (net_debug > 3)printk(KERN_DEBUG "%s: sent %ld byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);

		/* keep the upload from being interrupted, since we
                   ask the chip to start transmitting before the
                   whole packet has been completely uploaded. */
		save_flags(flags);
		cli();

		/* initiate a transmit sequence */
		writeword(dev, TX_CMD_PORT, lp->send_cmd);
		writeword(dev, TX_LEN_PORT, skb->len);

		/* Test to see if the chip has allocated memory for the packet */
		if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
			/* Gasp!  It hasn't.  But that shouldn't happen since
			   we're waiting for TxOk, so return 1 and requeue this packet. */
			lp->stats.tx_fifo_errors++;
			restore_flags(flags);
			return 1;
		}

		/* Write the contents of the packet */
		if (dev->mem_start) {
			memcpy((void *)dev->mem_start + PP_TxFrame, skb->data, (skb->len+1)&~1);
		} else
			outsw(ioaddr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);

		restore_flags(flags);
		dev->trans_start = jiffies;
	}
	dev_kfree_skb (skb, FREE_WRITE);

	return 0;
}


/* The typical workload of the driver:
   Handle the network interface interrupts. */
static void
net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
	struct device *dev = (struct device *)(irq2dev_map[irq]);
	struct net_local *lp;
	int ioaddr, status;

	if (dev == NULL) {
		printk (KERN_WARNING "net_interrupt(): irq %d for unknown device.\n", irq);
		return;
	}

	if (dev->interrupt)
		printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name);
	dev->interrupt = 1;

	ioaddr = dev->base_addr;
	lp = (struct net_local *)dev->priv;

	/* we MUST read all the events out of the ISQ, otherwise we'll never
           get interrupted again.  As a consequence, we can't have any limit
           on the number of times we loop in the interrupt handler.  The
           hardware guarantees that eventually we'll run out of events.  Of
           course, if you're on a slow machine, and packets are arriving
           faster than you can read them off, you're screwed.  Hasta la
           vista, baby!  */




	while ((status = readword(dev, ISQ_PORT))) {
		if (net_debug > 4)printk(KERN_DEBUG "%s: event=%04x\n", dev->name, status);


		if ( track_intr  < 5  ) {
			udelay(1000*30); 
		}
		++track_intr;
		switch(status & ISQ_EVENT_MASK) {
		case ISQ_RECEIVER_EVENT:
			/* Got a packet(s). */
			net_rx(dev);
			break;
		case ISQ_TRANSMITTER_EVENT:
			lp->stats.tx_packets++;
			dev->tbusy = 0;
			mark_bh(NET_BH);	/* Inform upper layers. */
			if ((status & TX_OK) == 0) lp->stats.tx_errors++;
			if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
			if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
			if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
			if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
			break;
		case ISQ_BUFFER_EVENT:
			if (status & READY_FOR_TX) {
				/* we tried to transmit a packet earlier,
                                   but inexplicably ran out of buffers.
                                   That shouldn't happen since we only ever
                                   load one packet.  Shrug.  Do the right
                                   thing anyway. */
				dev->tbusy = 0;
				mark_bh(NET_BH);	/* Inform upper layers. */
			}
			if (status & TX_UNDERRUN) {
                                lp->stats.tx_errors++;
                                lp->stats.tx_fifo_errors++;
				if (net_debug > 0) printk(KERN_NOTICE "%s: transmit underrun\n", dev->name);
                                lp->send_underrun++;
                                if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
                                else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
                        }
#if ALLOW_DMA
			if (status & RX_DMA) {
				int count = readreg(dev, PP_DmaFrameCnt);
				while(count) {
					if (net_debug > 5) printk(KERN_NOTICE "%s: receiving %d frames\n", dev->name, count);
					if (net_debug > 2 && count >1) printk(KERN_NOTICE "%s: receiving %d frames\n", dev->name, count);
					dma_rx(dev);
					if (--count == 0) count = readreg(dev, PP_DmaFrameCnt);
					if (net_debug > 2 && count >0) printk(KERN_NOTICE "%s: continuing with %d frames\n", dev->name, count);
				}
			}
#endif
			break;
		case ISQ_RX_MISS_EVENT:
			lp->stats.rx_missed_errors += (status >>6);
			break;
		case ISQ_TX_COL_EVENT:
			lp->stats.collisions += (status >>6);
			break;
		}
	}
	dev->interrupt = 0;
	return;
}

static void
count_rx_errors(int status, struct net_local *lp)
{
	lp->stats.rx_errors++;
	if (status & RX_RUNT) lp->stats.rx_length_errors++;
	if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
	if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
		/* per str 172 */
		lp->stats.rx_crc_errors++;
	if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
	return;
}

/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int ioaddr = dev->base_addr;
	struct sk_buff *skb;
	int status, length;

#if REQUIRE_MEMORY
	status = readw(dev->mem_start + PP_RxStatus);
	length = readw(dev->mem_start + PP_RxLength);
#else
	status = inw(ioaddr + RX_FRAME_PORT);
	length = inw(ioaddr + RX_FRAME_PORT);
#endif

	if ((status & RX_OK) == 0) {
		count_rx_errors(status, lp);
		return;
	}

	/* Malloc up new buffer. */
	skb = alloc_skb(length, GFP_ATOMIC);
	if (skb == NULL) {
		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
		lp->stats.rx_dropped++;
		return;
	}
	skb->len = length;
	skb->dev = dev;

	if (dev->mem_start) {
		memcpy(skb->data, (void *)dev->mem_start + PP_RxFrame, length);
	} else {
		insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
		if (length & 1)
			skb->data[length-1] = inw(ioaddr + RX_FRAME_PORT);
	}

	if (net_debug > 3)printk(KERN_DEBUG "%s: received %d byte packet of type %x\n",
                                 dev->name, length,
                                 (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
        skb->protocol=eth_type_trans(skb,dev);
	netif_rx(skb);
	lp->stats.rx_packets++;
	return;
}

/* The inverse routine to net_open(). */
static int
net_close(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;

	writereg(dev, PP_RxCFG, 0);
	writereg(dev, PP_TxCFG, 0);
	writereg(dev, PP_BufCFG, 0);
	writereg(dev, PP_BusCTL, 0);

	dev->start = 0;

	free_irq(dev->irq, NULL);
#if ALLOW_DMA
	if (dev->dma) {
		free_dma(dev->dma);
		free_pages((unsigned long)(lp->dma_buff), lp->dmasize==16?4:16);
	}
#endif

	irq2dev_map[dev->irq] = 0;

	/* Update the statistics here. */

        MOD_DEC_USE_COUNT;
	return 0;

}

/* Get the current statistics.	This may be called with the card open or
   closed. */
static struct enet_statistics *
net_get_stats(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;

	cli();
	/* Update the statistics from the device registers. */
	lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
	lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
	sti();

	return &lp->stats;
}

static void set_multicast_list(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;

	if(dev->flags&IFF_PROMISC)
	{
		lp->rx_mode = RX_ALL_ACCEPT;
	}
	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
	{
		/* The multicast-accept list is initialized to accept-all, and we
		   rely on higher-level filtering for now. */
		lp->rx_mode = RX_MULTCAST_ACCEPT;
	} 
	else
		lp->rx_mode = 0;

	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);

	/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
	writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
	     (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
}


static int
set_mac_address(struct device *dev, void *addr)
{
	int i;
	if (dev->start)
		return -EBUSY;
	printk(KERN_INFO "%s: Setting MAC address to ", dev->name);
	for (i = 0; i < 6; i++)
		printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
	printk(".\n");
	/* set the Ethernet address */
	for (i=0; i < ETH_ALEN/2; i++)
		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));

	return 0;
}
#ifdef MODULE
static char namespace[16] = "";
static struct device dev_cs89x0 = {
        NULL,
        0, 0, 0, 0,
        0, 0,
        0, 0, 0, NULL, NULL };

int io=0;
int irq=0;
#if ALLOW_DMA
int dma=0;
int dmasize=16;			/* or 64 */
#endif
int debug=1;
char *media="auto";
char *duplex="";

/*
* media=t             - specify media type
   or media=2
   or media=aui
   or medai=auto
* duplex=full            - specify forced half/full/autonegotiate duplex
   or duplex=half
   or duplex=auto


* Default Chip Configuration:
  * DMA Burst = enabled
  * IOCHRDY Enabled = enabled
    * UseSA = enabled
    * CS8900 defaults to half-duplex if not specified on command-line
    * CS8920 defaults to autoneg if not specified on command-line
    * Use reset defaults for other config parameters

* Assumptions:
  * media type specified is supported (circuitry is present)
  * if memory address is > 1MB, then required mem decode hw is present
  * if 10B-2, then agent other than driver will enable DC/DC converter
    (hw or software util)


*/

int
init_module(void)
{
	struct net_local *lp;

	net_debug = debug;
        dev_cs89x0.name = namespace;
	dev_cs89x0.irq = irq;
	dev_cs89x0.base_addr = io;
        dev_cs89x0.init = cs89x0_probe;
        dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
	memset(dev_cs89x0.priv, 0, sizeof(struct net_local));
	lp = (struct net_local *)dev_cs89x0.priv;
#if ALLOW_DMA
	lp->dma = dma;
	lp->dmasize = dmasize;
#endif

        if (strstr(media, "rj45")) lp->force |= FORCE_RJ45;
	if (strstr(media, "aui"))  lp->force |= FORCE_AUI;
	if (strstr(media, "bnc"))  lp->force |= FORCE_BNC;

        if (!strcmp(duplex, "auto")) lp->force |= FORCE_AUTO;
        if (!strcmp(duplex, "half")) lp->force |= FORCE_HALF;
        if (!strcmp(duplex, "full")) lp->force |= FORCE_FULL;

        if (io == 0)  {
                printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
                printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
                return -EPERM;
        }
#if ALLOW_DMA
        if (dmasize != 16 && dmasize != 64)  {
                printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize);
                return -EPERM;
        }
#endif
        if (register_netdev(&dev_cs89x0) != 0) {
                printk(KERN_WARNING "cs89x0.c: No card found at 0x%x\n", io);
                return -ENXIO;
        }
	return 0;
}

void
cleanup_module(void)
{

#if !REQUIRE_MEMORY
	outw(0, dev_cs89x0.base_addr + ADD_PORT);
#endif

        if (dev_cs89x0.priv != NULL) {
                /* Free up the private structure, or leak memory :-)  */
                kfree(dev_cs89x0.priv);
                dev_cs89x0.priv = NULL;	/* gets re-allocated by cs89x0_probe1 */
                /* If we don't do this, we can't re-insmod it later. */
                release_region(dev_cs89x0.base_addr, NETCARD_IO_EXTENT);
                unregister_netdev(&dev_cs89x0);
        }
}
#endif /* MODULE */


/*
 * Local variables:
 *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS -c cs89x0.c"
 *  version-control: t
 *  kept-new-versions: 5
 *  c-indent-level: 8
 *  c-basic-offset: 8
 *  tab-width: 8
 * End:
 */

/*  Copyright, 1988-1992, Russell Nelson, Crynwr Software

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, version 1.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */

#define PP_ChipID 0x0000	/* offset   0h -> Corp -ID              */
				/* offset   2h -> Model/Product Number  */
				/* offset   3h -> Chip Revision Number  */

#define PP_ISAIOB 0x0020	/*  IO base address */
#define PP_CS8900_ISAINT 0x0022	/*  ISA interrupt select */
#define PP_CS8920_ISAINT 0x0370	/*  ISA interrupt select */
#define PP_CS8900_ISADMA 0x0024	/*  ISA Rec DMA channel */
#define PP_CS8920_ISADMA 0x0374	/*  ISA Rec DMA channel */
#define PP_ISASOF 0x0026	/*  ISA DMA offset */
#define PP_DmaFrameCnt 0x0028	/*  ISA DMA Frame count */
#define PP_DmaByteCnt 0x002A	/*  ISA DMA Byte count */
#define PP_CS8900_ISAMemB 0x002C	/*  Memory base */
#define PP_CS8920_ISAMemB 0x0348 /*  */

#define PP_ISABootBase 0x0030	/*  Boot Prom base  */
#define PP_ISABootMask 0x0034	/*  Boot Prom Mask */

/* EEPROM data and command registers */
#define PP_EECMD 0x0040		/*  NVR Interface Command register */
#define PP_EEData 0x0042	/*  NVR Interface Data Register */
#define PP_DebugReg 0x0044	/*  Debug Register */

#define PP_RxCFG 0x0102		/*  Rx Bus config */
#define PP_RxCTL 0x0104		/*  Receive Control Register */
#define PP_TxCFG 0x0106		/*  Transmit Config Register */
#define PP_TxCMD 0x0108		/*  Transmit Command Register */
#define PP_BufCFG 0x010A	/*  Bus configuration Register */
#define PP_LineCTL 0x0112	/*  Line Config Register */
#define PP_SelfCTL 0x0114	/*  Self Command Register */
#define PP_BusCTL 0x0116	/*  ISA bus control Register */
#define PP_TestCTL 0x0118	/*  Test Register */
#define PP_AutoNegCTL 0x011C	/*  Auto Negotiation Ctrl */

#define PP_ISQ 0x0120		/*  Interrupt Status */
#define PP_RxEvent 0x0124	/*  Rx Event Register */
#define PP_TxEvent 0x0128	/*  Tx Event Register */
#define PP_BufEvent 0x012C	/*  Bus Event Register */
#define PP_RxMiss 0x0130	/*  Receive Miss Count */
#define PP_TxCol 0x0132		/*  Transmit Collision Count */
#define PP_LineST 0x0134	/*  Line State Register */
#define PP_SelfST 0x0136	/*  Self State register */
#define PP_BusST 0x0138		/*  Bus Status */
#define PP_TDR 0x013C		/*  Time Domain Reflectometry */
#define PP_AutoNegST 0x013E	/*  Auto Neg Status */
#define PP_TxCommand 0x0144	/*  Tx Command */
#define PP_TxLength 0x0146	/*  Tx Length */
#define PP_LAF 0x0150		/*  Hash Table */
#define PP_IA 0x0158		/*  Physical Address Register */

#define PP_RxStatus 0x0400	/*  Receive start of frame */
#define PP_RxLength 0x0402	/*  Receive Length of frame */
#define PP_RxFrame 0x0404	/*  Receive frame pointer */
#define PP_TxFrame 0x0A00	/*  Transmit frame pointer */

/*  Primary I/O Base Address. If no I/O base is supplied by the user, then this */
/*  can be used as the default I/O base to access the PacketPage Area. */
#define DEFAULTIOBASE 0x0300
#define FIRST_IO 0x020C		/*  First I/O port to check */
#define LAST_IO 0x037C		/*  Last I/O port to check (+10h) */
#define ADD_MASK 0x3000		/*  Mask it use of the ADD_PORT register */
#define ADD_SIG 0x3000		/*  Expected ID signature */

#define CHIP_EISA_ID_SIG 0x630E   /*  Product ID Code for Crystal Chip (CS8900 spec 4.3) */

#ifdef IBMEIPKT
#define EISA_ID_SIG 0x4D24	/*  IBM */
#define PART_NO_SIG 0x1010	/*  IBM */
#define MONGOOSE_BIT 0x0000	/*  IBM */
#else
#define EISA_ID_SIG 0x630E	/*  PnP Vendor ID (same as chip id for Crystal board) */
#define PART_NO_SIG 0x4000	/*  ID code CS8920 board (PnP Vendor Product code) */
#define MONGOOSE_BIT 0x2000	/*  PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */
#endif

#define PRODUCT_ID_ADD 0x0002   /*  Address of product ID */

/*  Mask to find out the types of  registers */
#define REG_TYPE_MASK 0x001F

/*  Eeprom Commands */
#define ERSE_WR_ENBL 0x00F0
#define ERSE_WR_DISABLE 0x0000

/*  Defines Control/Config register quintuplet numbers */
#define RX_BUF_CFG 0x0003
#define RX_CONTROL 0x0005
#define TX_CFG 0x0007
#define TX_COMMAND 0x0009
#define BUF_CFG 0x000B
#define LINE_CONTROL 0x0013
#define SELF_CONTROL 0x0015
#define BUS_CONTROL 0x0017
#define TEST_CONTROL 0x0019

/*  Defines Status/Count registers quintuplet numbers */
#define RX_EVENT 0x0004
#define TX_EVENT 0x0008
#define BUF_EVENT 0x000C
#define RX_MISS_COUNT 0x0010
#define TX_COL_COUNT 0x0012
#define LINE_STATUS 0x0014
#define SELF_STATUS 0x0016
#define BUS_STATUS 0x0018
#define TDR 0x001C

/* PP_RxCFG - Receive  Configuration and Interrupt Mask bit definition -  Read/write */
#define SKIP_1 0x0040
#define RX_STREAM_ENBL 0x0080
#define RX_OK_ENBL 0x0100
#define RX_DMA_ONLY 0x0200
#define AUTO_RX_DMA 0x0400
#define BUFFER_CRC 0x0800
#define RX_CRC_ERROR_ENBL 0x1000
#define RX_RUNT_ENBL 0x2000
#define RX_EXTRA_DATA_ENBL 0x4000

/* PP_RxCTL - Receive Control bit definition - Read/write */
#define RX_IA_HASH_ACCEPT 0x0040
#define RX_PROM_ACCEPT 0x0080
#define RX_OK_ACCEPT 0x0100
#define RX_MULTCAST_ACCEPT 0x0200
#define RX_IA_ACCEPT 0x0400
#define RX_BROADCAST_ACCEPT 0x0800
#define RX_BAD_CRC_ACCEPT 0x1000
#define RX_RUNT_ACCEPT 0x2000
#define RX_EXTRA_DATA_ACCEPT 0x4000
#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT)
/*  Default receive mode - individually addressed, broadcast, and error free */
#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT)

/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */
#define TX_LOST_CRS_ENBL 0x0040
#define TX_SQE_ERROR_ENBL 0x0080
#define TX_OK_ENBL 0x0100
#define TX_LATE_COL_ENBL 0x0200
#define TX_JBR_ENBL 0x0400
#define TX_ANY_COL_ENBL 0x0800
#define TX_16_COL_ENBL 0x8000

/* PP_TxCMD - Transmit Command bit definition - Read-only */
#define TX_START_4_BYTES 0x0000
#define TX_START_64_BYTES 0x0040
#define TX_START_128_BYTES 0x0080
#define TX_START_ALL_BYTES 0x00C0
#define TX_FORCE 0x0100
#define TX_ONE_COL 0x0200
#define TX_TWO_PART_DEFF_DISABLE 0x0400
#define TX_NO_CRC 0x1000
#define TX_RUNT 0x2000

/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */
#define GENERATE_SW_INTERRUPT 0x0040
#define RX_DMA_ENBL 0x0080
#define READY_FOR_TX_ENBL 0x0100
#define TX_UNDERRUN_ENBL 0x0200
#define RX_MISS_ENBL 0x0400
#define RX_128_BYTE_ENBL 0x0800
#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
#define RX_DEST_MATCH_ENBL 0x8000

/* PP_LineCTL - Line Control bit definition - Read/write */
#define SERIAL_RX_ON 0x0040
#define SERIAL_TX_ON 0x0080
#define AUI_ONLY 0x0100
#define AUTO_AUI_10BASET 0x0200
#define MODIFIED_BACKOFF 0x0800
#define NO_AUTO_POLARITY 0x1000
#define TWO_PART_DEFDIS 0x2000
#define LOW_RX_SQUELCH 0x4000

/* PP_SelfCTL - Software Self Control bit definition - Read/write */
#define POWER_ON_RESET 0x0040
#define SW_STOP 0x0100
#define SLEEP_ON 0x0200
#define AUTO_WAKEUP 0x0400
#define HCB0_ENBL 0x1000
#define HCB1_ENBL 0x2000
#define HCB0 0x4000
#define HCB1 0x8000

/* PP_BusCTL - ISA Bus Control bit definition - Read/write */
#define RESET_RX_DMA 0x0040
#define MEMORY_ON 0x0400
#define DMA_BURST_MODE 0x0800
#define IO_CHANNEL_READY_ON 0x1000
#define RX_DMA_SIZE_64K 0x2000
#define ENABLE_IRQ 0x8000

/* PP_TestCTL - Test Control bit definition - Read/write */
#define LINK_OFF 0x0080
#define ENDEC_LOOPBACK 0x0200
#define AUI_LOOPBACK 0x0400
#define BACKOFF_OFF 0x0800
#define FAST_TEST 0x8000

/* PP_RxEvent - Receive Event Bit definition - Read-only */
#define RX_IA_HASHED 0x0040
#define RX_DRIBBLE 0x0080
#define RX_OK 0x0100
#define RX_HASHED 0x0200
#define RX_IA 0x0400
#define RX_BROADCAST 0x0800
#define RX_CRC_ERROR 0x1000
#define RX_RUNT 0x2000
#define RX_EXTRA_DATA 0x4000

#define HASH_INDEX_MASK 0x0FC00

/* PP_TxEvent - Transmit Event Bit definition - Read-only */
#define TX_LOST_CRS 0x0040
#define TX_SQE_ERROR 0x0080
#define TX_OK 0x0100
#define TX_LATE_COL 0x0200
#define TX_JBR 0x0400
#define TX_16_COL 0x8000
#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS)
#define TX_COL_COUNT_MASK 0x7800

/* PP_BufEvent - Buffer Event Bit definition - Read-only */
#define SW_INTERRUPT 0x0040
#define RX_DMA 0x0080
#define READY_FOR_TX 0x0100
#define TX_UNDERRUN 0x0200
#define RX_MISS 0x0400
#define RX_128_BYTE 0x0800
#define TX_COL_OVRFLW 0x1000
#define RX_MISS_OVRFLW 0x2000
#define RX_DEST_MATCH 0x8000

/* PP_LineST - Ethernet Line Status bit definition - Read-only */
#define LINK_OK 0x0080
#define AUI_ON 0x0100
#define TENBASET_ON 0x0200
#define POLARITY_OK 0x1000
#define CRS_OK 0x4000

/* PP_SelfST - Chip Software Status bit definition */
#define ACTIVE_33V 0x0040
#define INIT_DONE 0x0080
#define SI_BUSY 0x0100
#define EEPROM_PRESENT 0x0200
#define EEPROM_OK 0x0400
#define EL_PRESENT 0x0800
#define EE_SIZE_64 0x1000

/* PP_BusST - ISA Bus Status bit definition */
#define TX_BID_ERROR 0x0080
#define READY_FOR_TX_NOW 0x0100

/* PP_AutoNegCTL - Auto Negotiation Control bit definition */
#define RE_NEG_NOW 0x0040
#define ALLOW_FDX 0x0080
#define AUTO_NEG_ENABLE 0x0100
#define NLP_ENABLE 0x0200
#define FORCE_FDX 0x8000
#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE)
#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW)

/* PP_AutoNegST - Auto Negotiation Status bit definition */
#define AUTO_NEG_BUSY 0x0080
#define FLP_LINK 0x0100
#define FLP_LINK_GOOD 0x0800
#define LINK_FAULT 0x1000
#define HDX_ACTIVE 0x4000
#define FDX_ACTIVE 0x8000

/*  The following block defines the ISQ event types */
#define ISQ_RECEIVER_EVENT 0x04
#define ISQ_TRANSMITTER_EVENT 0x08
#define ISQ_BUFFER_EVENT 0x0c
#define ISQ_RX_MISS_EVENT 0x10
#define ISQ_TX_COL_EVENT 0x12

#define ISQ_EVENT_MASK 0x003F   /*  ISQ mask to find out type of event */
#define ISQ_HIST 16		/*  small history buffer */
#define AUTOINCREMENT 0x8000	/*  Bit mask to set bit-15 for autoincrement */

#define TXRXBUFSIZE 0x0600
#define RXDMABUFSIZE 0x8000
#define RXDMASIZE 0x4000
#define TXRX_LENGTH_MASK 0x07FF

/*  rx options bits */
#define RCV_WITH_RXON	1       /*  Set SerRx ON */
#define RCV_COUNTS	2       /*  Use Framecnt1 */
#define RCV_PONG	4       /*  Pong respondent */
#define RCV_DONG	8       /*  Dong operation */
#define RCV_POLLING	0x10	/*  Poll RxEvent */
#define RCV_ISQ		0x20	/*  Use ISQ, int */
#define RCV_AUTO_DMA	0x100	/*  Set AutoRxDMAE */
#define RCV_DMA		0x200	/*  Set RxDMA only */
#define RCV_DMA_ALL	0x400	/*  Copy all DMA'ed */
#define RCV_FIXED_DATA	0x800	/*  Every frame same */
#define RCV_IO		0x1000	/*  Use ISA IO only */
#define RCV_MEMORY	0x2000	/*  Use ISA Memory */

#define RAM_SIZE	0x1000       /*  The card has 4k bytes or RAM */
#define PKT_START PP_TxFrame  /*  Start of packet RAM */

#define RX_FRAME_PORT	0x0000
#define TX_FRAME_PORT RX_FRAME_PORT
#define TX_CMD_PORT	0x0004
#define TX_NOW		0x0000       /*  Tx packet after   5 bytes copied */
#define TX_AFTER_381	0x0020       /*  Tx packet after 381 bytes copied */
#define TX_AFTER_ALL	0x0060       /*  Tx packet after all bytes copied */
#define TX_LEN_PORT	0x0006
#define ISQ_PORT	0x0008
#define ADD_PORT	0x000A
#define DATA_PORT	0x000C

#define EEPROM_WRITE_EN		0x00F0
#define EEPROM_WRITE_DIS	0x0000
#define EEPROM_WRITE_CMD	0x0100
#define EEPROM_READ_CMD		0x0200

/*  Receive Header */
/*  Description of header of each packet in receive area of memory */
#define RBUF_EVENT_LOW	0   /*  Low byte of RxEvent - status of received frame */
#define RBUF_EVENT_HIGH	1   /*  High byte of RxEvent - status of received frame */
#define RBUF_LEN_LOW	2   /*  Length of received data - low byte */
#define RBUF_LEN_HI	3   /*  Length of received data - high byte */
#define RBUF_HEAD_LEN	4   /*  Length of this header */

#define CHIP_READ 0x1   /*  Used to mark state of the repins code (chip or dma) */
#define DMA_READ 0x2   /*  Used to mark state of the repins code (chip or dma) */

/*  for bios scan */
/*  */
#ifdef CSDEBUG
/*  use these values for debugging bios scan */
#define BIOS_START_SEG 0x00000
#define BIOS_OFFSET_INC 0x0010
#else
#define BIOS_START_SEG 0x0c000
#define BIOS_OFFSET_INC 0x0200
#endif

#define BIOS_LAST_OFFSET 0x0fc00

/*  Byte offsets into the EEPROM configuration buffer */
#define ISA_CNF_OFFSET 0x6
#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8)			/*  8900 eeprom */
#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8)		/*  8920 eeprom */

  /*  the assumption here is that the bits in the eeprom are generally  */
  /*  in the same position as those in the autonegctl register. */
  /*  Of course the IMM bit is not in that register so it must be  */
  /*  masked out */
#define EE_FORCE_FDX  0x8000
#define EE_NLP_ENABLE 0x0200
#define EE_AUTO_NEG_ENABLE 0x0100
#define EE_ALLOW_FDX 0x0080
#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX)

#define IMM_BIT 0x0040		/*  ignore missing media	 */

#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2)
#define A_CNF_10B_T 0x0001
#define A_CNF_AUI 0x0002
#define A_CNF_10B_2 0x0004
#define A_CNF_MEDIA_TYPE 0x0060
#define A_CNF_MEDIA_AUTO 0x0000
#define A_CNF_MEDIA_10B_T 0x0020
#define A_CNF_MEDIA_AUI 0x0040
#define A_CNF_MEDIA_10B_2 0x0060
#define A_CNF_DC_DC_POLARITY 0x0080
#define A_CNF_NO_AUTO_POLARITY 0x2000
#define A_CNF_LOW_RX_SQUELCH 0x4000
#define A_CNF_EXTND_10B_2 0x8000

#define PACKET_PAGE_OFFSET 0x8

/*  Bit definitions for the ISA configuration word from the EEPROM */
#define INT_NO_MASK 0x000F
#define DMA_NO_MASK 0x0070
#define ISA_DMA_SIZE 0x0200
#define ISA_AUTO_RxDMA 0x0400
#define ISA_RxDMA 0x0800
#define DMA_BURST 0x1000
#define STREAM_TRANSFER 0x2000
#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA)

/*  DMA controller registers */
#define DMA_BASE 0x00     /*  DMA controller base */
#define DMA_BASE_2 0x0C0    /*  DMA controller base */

#define DMA_STAT 0x0D0    /*  DMA controller status register */
#define DMA_MASK 0x0D4    /*  DMA controller mask register */
#define DMA_MODE 0x0D6    /*  DMA controller mode register */
#define DMA_RESETFF 0x0D8    /*  DMA controller first/last flip flop */

/*  DMA data */
#define DMA_DISABLE 0x04     /*  Disable channel n */
#define DMA_ENABLE 0x00     /*  Enable channel n */
/*  Demand transfers, incr. address, auto init, writes, ch. n */
#define DMA_RX_MODE 0x14
/*  Demand transfers, incr. address, auto init, reads, ch. n */
#define DMA_TX_MODE 0x18

#define DMA_SIZE (16*1024) /*  Size of dma buffer - 16k */

#define CS8900 0x0000
#define CS8920 0x4000   
#define CS8920M 0x6000   
#define REVISON_BITS 0x1F00
#define EEVER_NUMBER 0x12
#define CHKSUM_LEN 0x14
#define CHKSUM_VAL 0x0000
#define START_EEPROM_DATA 0x001c /*  Offset into eeprom for start of data */
#define IRQ_MAP_EEPROM_DATA 0x0046 /*  Offset into eeprom for the IRQ map */
#define IRQ_MAP_LEN 0x0004 /*  No of bytes to read for the IRQ map */
#define PNP_IRQ_FRMT 0x0022 /*  PNP small item IRQ format */
#define CS8900_IRQ_MAP 0x1c20 /*  This IRQ map is fixed */

#define CS8920_NO_INTS 0x0F   /*  Max CS8920 interrupt select # */

#define PNP_ADD_PORT 0x0279
#define PNP_WRITE_PORT 0x0A79

#define GET_PNP_ISA_STRUCT 0x40
#define PNP_ISA_STRUCT_LEN 0x06
#define PNP_CSN_CNT_OFF 0x01
#define PNP_RD_PORT_OFF 0x02
#define PNP_FUNCTION_OK 0x00
#define PNP_WAKE 0x03
#define PNP_RSRC_DATA 0x04
#define PNP_RSRC_READY 0x01
#define PNP_STATUS 0x05
#define PNP_ACTIVATE 0x30
#define PNP_CNF_IO_H 0x60
#define PNP_CNF_IO_L 0x61
#define PNP_CNF_INT 0x70
#define PNP_CNF_DMA 0x74
#define PNP_CNF_MEM 0x48

#define BIT0 1
#define BIT15 0x8000