summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/motorola_powerpc/dec21140/dec21140.c
blob: 00c87c4358ac54840d87dc5def956f3c69c7a242 (plain) (tree)











































































































                                                                        
                                                









                                                                        
                            
























                                                                               

                                            






                                                
                                      
































                                                        
                               













































                                                                           
                                                                             










































                                                                                      
                                                                                         
















































                                                                              
                                                               

















                                                                                    
                                









                                                                                    
                                         









                                                             
                               

                                                   
                                                              
                                    

                                 


































                                                                   
                                                          





                    
                                                                                                                
                      
                                                 
                                                                                                          






                                                                     
                                



                                                                               


                                                                  


                             



                                                           



                    


                                                                          

























                                                                        
                                               
















                                                              
                                      





























                                                                       
                               



                                                                          
                          























                                                                       
                                                                    
















                                                 
                               

                   
                               









                                            
                                        


                    
                                                      































































































                                                                                                               
                               









                                        
                              



























































































                                                                        














































































































                                                                                       
/*
 *  RTEMS driver for TULIP based Ethernet Controller
 *
 *  Copyright (C) 1999 Emmanuel Raguet. raguet@crf.canon.fr
 *
 *  The license and distribution terms for this file may be
 *  found in found in the file LICENSE in this distribution or at
 *  http://www.OARcorp.com/rtems/license.html.
 *
 * $Id$
 */

#include <bsp.h>
#include <bsp/pci.h>

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <rtems/error.h>
#include <rtems/rtems_bsdnet.h>

#include <libcpu/cpu.h>
#include <libcpu/io.h>
#include <libcpu/byteorder.h>

#include <sys/param.h>
#include <sys/mbuf.h>

#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
 
#include <bsp/irq.h>

#ifdef malloc
#undef malloc
#endif
#ifdef free
#undef free 
#endif

#define DEC_DEBUG

#define PCI_INVALID_VENDORDEVICEID	0xffffffff
#define PCI_VENDOR_ID_DEC 0x1011
#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009

#define IO_MASK  0x3
#define MEM_MASK  0xF
#define MASK_OFFSET 0xF

/* command and status registers, 32-bit access, only if IO-ACCESS */
#define ioCSR0  0x00	/* bus mode register */
#define ioCSR1  0x08	/* transmit poll demand */
#define ioCSR2  0x10	/* receive poll demand */
#define ioCSR3  0x18	/* receive list base address */
#define ioCSR4  0x20	/* transmit list base address */
#define ioCSR5  0x28	/* status register */
#define ioCSR6  0x30	/* operation mode register */
#define ioCSR7  0x38	/* interrupt mask register */
#define ioCSR8  0x40	/* missed frame counter */
#define ioCSR9  0x48	/* Ethernet ROM register */
#define ioCSR10 0x50	/* reserved */
#define ioCSR11 0x58	/* full-duplex register */
#define ioCSR12 0x60	/* SIA status register */
#define ioCSR13 0x68
#define ioCSR14 0x70
#define ioCSR15 0x78	/* SIA general register */

/* command and status registers, 32-bit access, only if MEMORY-ACCESS */
#define memCSR0  0x00	/* bus mode register */
#define memCSR1  0x02	/* transmit poll demand */
#define memCSR2  0x04	/* receive poll demand */
#define memCSR3  0x06	/* receive list base address */
#define memCSR4  0x08	/* transmit list base address */
#define memCSR5  0x0A	/* status register */
#define memCSR6  0x0C	/* operation mode register */
#define memCSR7  0x0E	/* interrupt mask register */
#define memCSR8  0x10	/* missed frame counter */
#define memCSR9  0x12	/* Ethernet ROM register */
#define memCSR10 0x14	/* reserved */
#define memCSR11 0x16	/* full-duplex register */
#define memCSR12 0x18	/* SIA status register */
#define memCSR13 0x1A
#define memCSR14 0x1C
#define memCSR15 0x1E	/* SIA general register */

#define DEC_REGISTER_SIZE    0x100   /* to reserve virtual memory */

#define RESET_CHIP   0x00000001
#define CSR0_MODE    0x01b08000   /* 01a08000 */
#define ROM_ADDRESS  0x00004800
#define CSR6_INIT    0x020c0000   /* 020c0000 */  
#define CSR6_TX      0x00002000   
#define CSR6_TXRX    0x00002002   
#define IT_SETUP     0x00010040   /* 0001ebef */
#define CLEAR_IT     0xFFFFFFFF   
#define NO_IT        0x00000000   

#define NRXBUFS 7	/* number of receive buffers */
#define NTXBUFS 1	/* number of transmit buffers */

/* message descriptor entry */
struct MD {
    volatile unsigned long status;
    volatile unsigned long counts;
    volatile unsigned char *buf1, *buf2;	
};

/*
 * Number of WDs supported by this driver
 */
#define NDECDRIVER	1

/*
 * Receive buffer size -- Allow for a full ethernet packet including CRC
 */
#define RBUF_SIZE	1536

#define	ET_MINLEN 60		/* minimum message length */

/*
 * RTEMS event used by interrupt handler to signal driver tasks.
 * This must not be any of the events used by the network task synchronization.
 */
#define INTERRUPT_EVENT	RTEMS_EVENT_1

/*
 * RTEMS event used to start transmit daemon.
 * This must not be the same as INTERRUPT_EVENT.
 */
#define START_TRANSMIT_EVENT	RTEMS_EVENT_2

#if (MCLBYTES < RBUF_SIZE)
# error "Driver must have MCLBYTES > RBUF_SIZE"
#endif

/*
 * Per-device data
 */
 struct dec21140_softc {
  struct arpcom			arpcom;
  rtems_irq_connect_data	irqInfo;
  volatile struct MD		*MDbase;
  volatile unsigned char	*bufferBase;
  int				acceptBroadcast;
  int				rxBdCount;
  int				txBdCount;
  rtems_id			rxDaemonTid;
  rtems_id			txDaemonTid;

  unsigned int 			port;
  volatile unsigned int		*base;
  unsigned long			bpar;
   
  /*
   * Statistics
   */
  unsigned long	rxInterrupts;
  unsigned long	rxNotFirst;
  unsigned long	rxNotLast;
  unsigned long	rxGiant;
  unsigned long	rxNonOctet;
  unsigned long	rxRunt;
  unsigned long	rxBadCRC;
  unsigned long	rxOverrun;
  unsigned long	rxCollision;
  
  unsigned long	txInterrupts;
  unsigned long	txDeferred;
  unsigned long	txHeartbeat;
  unsigned long	txLateCollision;
  unsigned long	txRetryLimit;
  unsigned long	txUnderrun;
  unsigned long	txLostCarrier;
  unsigned long	txRawWait;
};

static struct dec21140_softc dec21140_softc[NDECDRIVER];

/*
 * DEC21140 interrupt handler
 */
static rtems_isr
dec21140Enet_interrupt_handler (rtems_vector_number v)
{
  volatile unsigned int *tbase;
  unsigned long status;

  unsigned int sc;
  
  tbase = dec21140_softc[0].base ;

  /*
   * Read status
   */
  st_le32((tbase+memCSR7), NO_IT);
  status = ld_le32(tbase+memCSR5);
  st_le32((tbase+memCSR5), CLEAR_IT);

  /*
   * Frame received?
   */
  if (status & 0x00000040){
    dec21140_softc[0].rxInterrupts++;
    sc = rtems_event_send (dec21140_softc[0].rxDaemonTid, INTERRUPT_EVENT);
  }
}

static void nopOn(const rtems_irq_connect_data* notUsed)
{
  /*
   * code should be moved from dec21140Enet_initialize_hardware
   * to this location
   */
}

static int dec21140IsOn(const rtems_irq_connect_data* irq)
{
  return BSP_irq_enabled_at_i8259s (irq->name);
}

/*
 * Read and write the MII registers using software-generated serial
 * MDIO protocol.
 */
#define MDIO_SHIFT_CLK		0x10000
#define MDIO_DATA_WRITE0 	0x00000
#define MDIO_DATA_WRITE1 	0x20000
#define MDIO_ENB		0x00000	 
#define MDIO_ENB_IN		0x40000
#define MDIO_DATA_READ		0x80000

static int mdio_read(volatile unsigned int *ioaddr, int phy_id, int location)
{
	int i, i3;
	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
	unsigned short retval = 0;

	/* Establish sync by sending at least 32 logic ones. */ 
	for (i = 32; i >= 0; i--) {
		st_le32(ioaddr, MDIO_ENB | MDIO_DATA_WRITE1);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
	}
	/* Shift the read command bits out. */
	for (i = 17; i >= 0; i--) {
		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
		st_le32(ioaddr, dataval);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, dataval | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, dataval);
		for(i3=0; i3<1000; i3++);
	}
	st_le32(ioaddr, MDIO_ENB_IN | MDIO_SHIFT_CLK);
	for(i3=0; i3<1000; i3++);
	st_le32(ioaddr, MDIO_ENB_IN);

	for (i = 16; i > 0; i--) {
		st_le32(ioaddr, MDIO_ENB_IN | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
		retval = (retval << 1) | ((ld_le32(ioaddr) & MDIO_DATA_READ) ? 1 : 0);
		st_le32(ioaddr,  MDIO_ENB_IN);
		for(i3=0; i3<1000; i3++);
	}
	/* Clear out extra bits. */
	for (i = 16; i > 0; i--) {
		st_le32(ioaddr, MDIO_ENB_IN | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, MDIO_ENB_IN);
		for(i3=0; i3<1000; i3++);
	}
	return ( ((retval<<8)&0xff00) | ((retval>>8)&0xff) );
}

static int mdio_write(volatile unsigned int *ioaddr, int phy_id, int location, int value)
{
	int i, i3;
	int cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;

	/* Establish sync by sending at least 32 logic ones. */ 
	for (i = 32; i >= 0; i--) {
		st_le32(ioaddr, MDIO_ENB | MDIO_DATA_WRITE1);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
	}
	/* Shift the read command bits out. */
	for (i = 31; i >= 0; i--) {
		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
		st_le32(ioaddr, dataval);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, dataval | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
	}

	/* Clear out extra bits. */
	for (i = 2; i > 0; i--) {
		st_le32(ioaddr, MDIO_ENB_IN);
		for(i3=0; i3<1000; i3++);
		st_le32(ioaddr, MDIO_ENB_IN | MDIO_SHIFT_CLK);
		for(i3=0; i3<1000; i3++);
	}
	return 0;


}

/*
 * This routine reads a word (16 bits) from the serial EEPROM.
 */
/*  EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK		0x02	/* EEPROM shift clock. */
#define EE_CS			0x01	/* EEPROM chip select. */
#define EE_DATA_WRITE		0x04	/* EEPROM chip data in. */
#define EE_WRITE_0		0x01
#define EE_WRITE_1		0x05
#define EE_DATA_READ		0x08	/* EEPROM chip data out. */
#define EE_ENB			(0x4800 | EE_CS)

/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD	(5 << 6)
#define EE_READ_CMD	(6 << 6)
#define EE_ERASE_CMD	(7 << 6)

static int eeget16(volatile unsigned int *ioaddr, int location)
{
	int i, i3;
	unsigned short retval = 0;
	int read_cmd = location | EE_READ_CMD;
	
	st_le32(ioaddr, EE_ENB & ~EE_CS);
	st_le32(ioaddr, EE_ENB);
	
	/* Shift the read command bits out. */
	for (i = 10; i >= 0; i--) {
		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
		st_le32(ioaddr, EE_ENB | dataval);
		for (i3=0; i3<1000; i3++) ;
		st_le32(ioaddr, EE_ENB | dataval | EE_SHIFT_CLK);
		for (i3=0; i3<1000; i3++) ;
		st_le32(ioaddr, EE_ENB | dataval); /* Finish EEPROM a clock tick. */
		for (i3=0; i3<1000; i3++) ;
	}
	st_le32(ioaddr, EE_ENB);
	
	for (i = 16; i > 0; i--) {
		st_le32(ioaddr, EE_ENB | EE_SHIFT_CLK);
		for (i3=0; i3<1000; i3++) ;
		retval = (retval << 1) | ((ld_le32(ioaddr) & EE_DATA_READ) ? 1 : 0);
		st_le32(ioaddr, EE_ENB);
		for (i3=0; i3<1000; i3++) ;
	}

	/* Terminate the EEPROM access. */
	st_le32(ioaddr, EE_ENB & ~EE_CS);
	return ( ((retval<<8)&0xff00) | ((retval>>8)&0xff) );
}

/*
 * Initialize the ethernet hardware
 */
static void
dec21140Enet_initialize_hardware (struct dec21140_softc *sc)
{
  rtems_status_code st;
  volatile unsigned int *tbase;
  union {char c[64]; unsigned short s[32];} rombuf;
  int i, i2, i3;
  volatile unsigned char *cp, direction, *setup_frm, *eaddrs; 
  unsigned long csr12_val, mii_reg0;
  volatile unsigned char *buffer;
  volatile struct MD *rmd;

  
  tbase = sc->base;



  /*
   * WARNING : First write in CSR6
   *           Then Reset the chip ( 1 in CSR0)
   */

  st_le32( (tbase+memCSR6), CSR6_INIT);  
  st_le32( (tbase+memCSR0), RESET_CHIP);  
  for(i3=0; i3<1000; i3++);

  /*
   * Init CSR0
   */
  st_le32( (tbase+memCSR0), CSR0_MODE);  

  csr12_val = ld_le32( (tbase+memCSR8) );

  for (i=0; i<32; i++){
    rombuf.s[i] = eeget16(tbase+memCSR9, i);
  }
  memcpy (sc->arpcom.ac_enaddr, rombuf.c+20, ETHER_ADDR_LEN);

  mii_reg0 = mdio_read(tbase+memCSR9, 0, 0); 
  mdio_write(tbase+memCSR9, 0, 0, mii_reg0 | 0x1000);

#ifdef DEC_DEBUG
  printk("DC21140 %x:%x:%x:%x:%x:%x IRQ %d IO %x M %x .........\n",
	 sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1],
	 sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3],
	 sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5],
	 sc->irqInfo.name, sc->port, (unsigned) sc->base);
#endif
  
  /*
   * Init RX ring
   */
  sc->rxBdCount = 0;
  cp = (volatile unsigned char *)malloc((NRXBUFS+NTXBUFS)*(sizeof(struct MD)+ RBUF_SIZE) + PPC_CACHE_ALIGNMENT);
  sc->bufferBase = cp;
  if ((unsigned int)cp & (PPC_CACHE_ALIGNMENT-1))
    cp = (volatile unsigned char *) (((unsigned int)cp + PPC_CACHE_ALIGNMENT) & ~(PPC_CACHE_ALIGNMENT-1));
#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA 
  if (_CPU_is_paging_enabled())
    _CPU_change_memory_mapping_attribute
                   (NULL, cp,
		    (NRXBUFS+NTXBUFS)*(sizeof(struct MD)+ RBUF_SIZE),
		    PTE_CACHE_DISABLE | PTE_WRITABLE);
#endif
  rmd = (volatile struct MD*)cp;
  sc->MDbase = rmd;
  buffer = cp + ((NRXBUFS+NTXBUFS)*sizeof(struct MD));
  st_le32( (tbase+memCSR3), (long)((long)(sc->MDbase) + PREP_PCI_DRAM_OFFSET));
  for (i=0 ; i<NRXBUFS; i++){
    rmd->buf2 = (volatile unsigned char *) 0;
    rmd->buf1 = (buffer + (i*RBUF_SIZE) + PREP_PCI_DRAM_OFFSET);  
    rmd->counts = 0xfcc00000 | (RBUF_SIZE);
    rmd->status = 0x80000000;
    rmd++;
  }
  /*
   * mark last RX buffer.
   */
  sc->MDbase [NRXBUFS-1].counts = 0xfec00000 | (RBUF_SIZE);
  /*
   * Init TX ring
   */
  sc->txBdCount = 0;
  st_le32( (tbase+memCSR4), (long)(((long)(rmd)) + PREP_PCI_DRAM_OFFSET));
  rmd->buf2 = (volatile unsigned char *) 0;
  rmd->buf1 = buffer + (NRXBUFS*RBUF_SIZE) + PREP_PCI_DRAM_OFFSET;
  rmd->counts = 0x62000000;
  rmd->status = 0x0;
  
  /*
   * Set up interrupts
   */
  st_le32( (tbase+memCSR5), IT_SETUP);
  st_le32( (tbase+memCSR7), IT_SETUP); 

  sc->irqInfo.hdl = (rtems_irq_hdl)dec21140Enet_interrupt_handler;
  sc->irqInfo.on  = nopOn;
  sc->irqInfo.off = nopOn;
  sc->irqInfo.isOn = dec21140IsOn;  
  st = BSP_install_rtems_irq_handler (&sc->irqInfo);
  if (!st)
    rtems_panic ("Can't attach DEC21140 interrupt handler for irq %d\n",
		  sc->irqInfo.name);

  /*
   * Start TX for setup frame
   */
  st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TX);

  /*
   * Build setup frame
   */
  setup_frm = rmd->buf1 - PREP_PCI_DRAM_OFFSET;
  eaddrs = (char *)(sc->arpcom.ac_enaddr);
  /* Fill the buffer with our physical address. */
  for (i = 1; i < 16; i++) {
	*setup_frm++ = eaddrs[0];
	*setup_frm++ = eaddrs[1];
	*setup_frm++ = eaddrs[0];
	*setup_frm++ = eaddrs[1];
	*setup_frm++ = eaddrs[2];
	*setup_frm++ = eaddrs[3];
	*setup_frm++ = eaddrs[2];
	*setup_frm++ = eaddrs[3];
	*setup_frm++ = eaddrs[4];
	*setup_frm++ = eaddrs[5];
	*setup_frm++ = eaddrs[4];
	*setup_frm++ = eaddrs[5];
  }
  /* Add the broadcast address when doing perfect filtering */
  memset((void*) setup_frm, 0xff, 12);
  rmd->counts = 0x0a000000 | 192 ;
  rmd->status = 0x80000000;
  st_le32( (tbase+memCSR1), 1);
  while (rmd->status != 0x7fffffff);

  /*
   * Enable RX and TX
   */
  st_le32( (unsigned int*)(tbase+memCSR6), CSR6_INIT | CSR6_TXRX);
  
  /*
   * Set up PHY
   */
  
  i = rombuf.c[27];
  i+=2;
  direction = rombuf.c[i];
  i +=4;
  st_le32( (tbase+memCSR12), direction | 0x100);
  for (i2 = 0; i2 < rombuf.c[(i+2) + rombuf.c[i+1]]; i2++){
    st_le32( (tbase + memCSR12), rombuf.c[(i+3) + rombuf.c[i+1] + i2]);
  }
  for (i2 = 0; i2 < rombuf.c[i+1]; i2++){
    st_le32( (tbase + memCSR12), rombuf.c[(i+2) + i2]);
  }
}

static void
dec21140_rxDaemon (void *arg)
{
  volatile unsigned int *tbase;
  struct ether_header *eh;
  struct dec21140_softc *dp = (struct dec21140_softc *)&dec21140_softc[0];
  struct ifnet *ifp = &dp->arpcom.ac_if;
  struct mbuf *m;
  volatile struct MD *rmd;
  unsigned int len;
  char *temp;
  rtems_event_set events;
  int nbMD;
  
  tbase = dec21140_softc[0].base ;

  for (;;){

    rtems_bsdnet_event_receive (INTERRUPT_EVENT,
				RTEMS_WAIT|RTEMS_EVENT_ANY,
				RTEMS_NO_TIMEOUT,
				&events);
    rmd = dec21140_softc[0].MDbase;
    nbMD = 0;
    
    while (nbMD < NRXBUFS){
      if ( (rmd->status & 0x80000000) == 0){
	len = (rmd->status >> 16) & 0x7ff;
	MGETHDR (m, M_WAIT, MT_DATA);
	MCLGET (m, M_WAIT);
	m->m_pkthdr.rcvif = ifp;
	temp = m->m_data;
	m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
	memcpy(temp, (void*) (rmd->buf1-PREP_PCI_DRAM_OFFSET), len);
	rmd->status = 0x80000000;
	eh = mtod (m, struct ether_header *);
	m->m_data += sizeof(struct ether_header);
	ether_input (ifp, eh, m);
      }
      rmd++;
      nbMD++;
    }
    st_le32( (tbase+memCSR7), IT_SETUP); 
  }	
}

static void
sendpacket (struct ifnet *ifp, struct mbuf *m)
{
  struct dec21140_softc *dp = ifp->if_softc;
  volatile struct MD *tmd;
  volatile unsigned char *temp;
  struct mbuf *n;
  unsigned int len;
  volatile unsigned int *tbase;

  tbase = dp->base;

  /*
   * Waiting for Transmitter ready
   */	
  tmd = dec21140_softc[0].MDbase + NRXBUFS;
  while ( (tmd->status & 0x80000000) != 0 );
  len = 0;
  n = m;
  temp = tmd->buf1-PREP_PCI_DRAM_OFFSET;
  
  for (;;){
    len += m->m_len;
    memcpy((void*) temp, (char *)m->m_data, m->m_len);
    temp += m->m_len ;
    if ((m = m->m_next) == NULL)
      break;
  }

  if (len < ET_MINLEN) len = ET_MINLEN;
  tmd->counts = 0xe2000000 | len;
  tmd->status = 0x80000000;

  st_le32( (tbase+memCSR1), 0x1);

  m_freem(n);
}

/*
 * Driver transmit daemon
 */
void
dec21140_txDaemon (void *arg)
{
  struct dec21140_softc *sc = (struct dec21140_softc *)arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  struct mbuf *m;
  rtems_event_set events;

  for (;;) {
    /*
     * Wait for packet
     */

    rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);

    /*
     * Send packets till queue is empty
     */
    for (;;) {
      /*
       * Get the next mbuf chain to transmit.
       */
      IF_DEQUEUE(&ifp->if_snd, m);
      if (!m)
	break;
      sendpacket (ifp, m);
    }
    ifp->if_flags &= ~IFF_OACTIVE;
  }
}	


static void
dec21140_start (struct ifnet *ifp)
{
	struct dec21140_softc *sc = ifp->if_softc;

	rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
	ifp->if_flags |= IFF_OACTIVE;
}

/*
 * Initialize and start the device
 */
static void
dec21140_init (void *arg)
{
  struct dec21140_softc *sc = arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;

  if (sc->txDaemonTid == 0) {
    
    /*
     * Set up DEC21140 hardware
     */
    dec21140Enet_initialize_hardware (sc);
    
    /*
     * Start driver tasks
     */
    sc->rxDaemonTid = rtems_bsdnet_newproc ("DCrx", 4096,
					    dec21140_rxDaemon, sc);
    sc->txDaemonTid = rtems_bsdnet_newproc ("DCtx", 4096,
					    dec21140_txDaemon, sc);
  }

  /*
   * Tell the world that we're running.
   */
  ifp->if_flags |= IFF_RUNNING;

}

/*
 * Stop the device
 */
static void
dec21140_stop (struct dec21140_softc *sc)
{
  volatile unsigned int *tbase;
  struct ifnet *ifp = &sc->arpcom.ac_if;

  ifp->if_flags &= ~IFF_RUNNING;

  /*
   * Stop the transmitter
   */
  tbase=dec21140_softc[0].base ;
  st_le32( (tbase+memCSR7), NO_IT);
  st_le32( (tbase+memCSR6), CSR6_INIT);
  free((void*)sc->bufferBase);
}


/*
 * Show interface statistics
 */
static void
dec21140_stats (struct dec21140_softc *sc)
{
	printf ("      Rx Interrupts:%-8lu", sc->rxInterrupts);
	printf ("       Not First:%-8lu", sc->rxNotFirst);
	printf ("        Not Last:%-8lu\n", sc->rxNotLast);
	printf ("              Giant:%-8lu", sc->rxGiant);
	printf ("            Runt:%-8lu", sc->rxRunt);
	printf ("       Non-octet:%-8lu\n", sc->rxNonOctet);
	printf ("            Bad CRC:%-8lu", sc->rxBadCRC);
	printf ("         Overrun:%-8lu", sc->rxOverrun);
	printf ("       Collision:%-8lu\n", sc->rxCollision);

	printf ("      Tx Interrupts:%-8lu", sc->txInterrupts);
	printf ("        Deferred:%-8lu", sc->txDeferred);
	printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
	printf ("         No Carrier:%-8lu", sc->txLostCarrier);
	printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
	printf ("  Late Collision:%-8lu\n", sc->txLateCollision);
	printf ("           Underrun:%-8lu", sc->txUnderrun);
	printf (" Raw output wait:%-8lu\n", sc->txRawWait);
}

/*
 * Driver ioctl handler
 */
static int
dec21140_ioctl (struct ifnet *ifp, int command, caddr_t data)
{
	struct dec21140_softc *sc = ifp->if_softc;
	int error = 0;

	switch (command) {
	case SIOCGIFADDR:
	case SIOCSIFADDR:
		ether_ioctl (ifp, command, data);
		break;

	case SIOCSIFFLAGS:
		switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
		case IFF_RUNNING:
			dec21140_stop (sc);
			break;

		case IFF_UP:
			dec21140_init (sc);
			break;

		case IFF_UP | IFF_RUNNING:
			dec21140_stop (sc);
			dec21140_init (sc);
			break;

		default:
			break;
		}
		break;

	case SIO_RTEMS_SHOW_STATS:
		dec21140_stats (sc);
		break;
		
	/*
	 * FIXME: All sorts of multicast commands need to be added here!
	 */
	default:
		error = EINVAL;
		break;
	}

	return error;
}

/*
 * Attach an DEC21140 driver to the system
 */
int
rtems_dec21140_driver_attach (struct rtems_bsdnet_ifconfig *config)
{
	struct dec21140_softc *sc;
	struct ifnet *ifp;
	int mtu;
	int i;
	unsigned char ucSlotNumber, ucFnNumber;
	unsigned int  ulDeviceID, lvalue, tmp;	
	unsigned char cvalue;
	
	/*
	 * First, find a DEC board
	 */
	for(ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) {
	  for(ucFnNumber=0;ucFnNumber<PCI_MAX_FUNCTIONS;ucFnNumber++) {
	    (void)pci_read_config_dword(0,
					ucSlotNumber,
					ucFnNumber,
					PCI_VENDOR_ID,
					&ulDeviceID);
	    if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) {
	      /*
	       * This slot is empty
	       */
	      continue;
	    }
	    if (ulDeviceID == ((PCI_DEVICE_ID_DEC_TULIP_FAST<<16) + PCI_VENDOR_ID_DEC))
	      break;
	  }
	  if (ulDeviceID == ((PCI_DEVICE_ID_DEC_TULIP_FAST<<16) + PCI_VENDOR_ID_DEC)){
	    printk("DEC Adapter found !!\n");
	    break;
	  }
	}
	
	if(ulDeviceID==PCI_INVALID_VENDORDEVICEID)
	  rtems_panic("DEC PCI board not found !!\n");
  
	/*
	 * Find a free driver
	 */
	for (i = 0 ; i < NDECDRIVER ; i++) {
		sc = &dec21140_softc[i];
		ifp = &sc->arpcom.ac_if;
		if (ifp->if_softc == NULL)
			break;
	}
	if (i >= NDECDRIVER) {
		printk ("Too many DEC drivers.\n");
		return 0;
	}

	/*
	 * Process options
	 */

	(void)pci_read_config_dword(0,
				    ucSlotNumber,
				    ucFnNumber,
				    PCI_BASE_ADDRESS_0,
				    &lvalue);

	sc->port = lvalue & (unsigned int)(~IO_MASK);
        
	(void)pci_read_config_dword(0,
				    ucSlotNumber,
				    ucFnNumber,
				    PCI_BASE_ADDRESS_1  ,
				    &lvalue);


	tmp = (unsigned int)(lvalue & (unsigned int)(~MEM_MASK)) 
	  + (unsigned int)PREP_ISA_MEM_BASE;
	sc->base = (unsigned int *)(tmp);

	(void)pci_read_config_byte(0,
				   ucSlotNumber,
				   ucFnNumber,
				   PCI_INTERRUPT_LINE,
				   &cvalue);
	sc->irqInfo.name = (rtems_irq_symbolic_name)cvalue;

 	if (config->hardware_address) {
	  memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
		  ETHER_ADDR_LEN);
	}
	else {
	  memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
	}
	if (config->mtu)
		mtu = config->mtu;
	else
		mtu = ETHERMTU;

	sc->acceptBroadcast = !config->ignore_broadcast;

	/*
	 * Set up network interface values
	 */
	ifp->if_softc = sc;
	ifp->if_unit = i + 1;
	ifp->if_name = "dc";
	ifp->if_mtu = mtu;
	ifp->if_init = dec21140_init;
	ifp->if_ioctl = dec21140_ioctl;
	ifp->if_start = dec21140_start;
	ifp->if_output = ether_output;
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
	if (ifp->if_snd.ifq_maxlen == 0)
		ifp->if_snd.ifq_maxlen = ifqmaxlen;

 	/*
	 * Attach the interface
	 */
	if_attach (ifp);
	ether_ifattach (ifp);

	return 1;
};