OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [rtems/] [c/] [src/] [lib/] [libbsp/] [i386/] [pc386/] [wd8003/] [wd8003.c] - Rev 602

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * RTEMS driver for WD800x
 *
 *  Based on the 68360 Network Driver by:
 *    W. Eric Norum
 *    Saskatchewan Accelerator Laboratory
 *    University of Saskatchewan
 *    Saskatoon, Saskatchewan, CANADA
 *    eric@skatter.usask.ca
 *
 *  $Id: wd8003.c,v 1.2 2001-09-27 11:59:49 chris Exp $
 */
 
#include <bsp.h>
#include <wd80x3.h>
 
#include <stdio.h>
#include <stdarg.h>
#include <rtems/error.h>
#include <rtems/rtems_bsdnet.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 <irq.h>
 
#define	ET_MINLEN 60		/* minimum message length */
 
/*
 * Number of WDs supported by this driver
 */
#define NWDDRIVER	1
 
/*
 * Default number of buffer descriptors set aside for this driver.
 * The number of transmit buffer descriptors has to be quite large
 * since a single frame often uses four or more buffer descriptors.
 */
#define RX_BUF_COUNT     15
#define TX_BUF_COUNT     4
#define TX_BD_PER_BUF    4
 
/*
 * 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
 
/*
 * Receive buffer size -- Allow for a full ethernet packet including CRC
 */
#define RBUF_SIZE	1520
 
#if (MCLBYTES < RBUF_SIZE)
# error "Driver must have MCLBYTES > RBUF_SIZE"
#endif
 
/*
 * Per-device data
 */
struct wd_softc {
  struct arpcom			arpcom;
  rtems_irq_connect_data	irqInfo;
  struct mbuf			**rxMbuf;
  struct mbuf			**txMbuf;
  int				acceptBroadcast;
  int				rxBdCount;
  int				txBdCount;
  int				txBdHead;
  int				txBdTail;
  int				txBdActiveCount;
  rtems_id			rxDaemonTid;
  rtems_id			txDaemonTid;
 
  unsigned int 			port;
  unsigned char			*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;
};
 
#define RO 0x10
 
#define SHATOT (8*1024)		/* size of shared memory */
#define SHAPAGE 256		/* shared memory information */
#define MAXSIZ 	1536		/*(MAXBUF - MESSH_SZ)*/
#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
 
static volatile unsigned long overrun;
static volatile unsigned long resend;
static struct wd_softc wd_softc[NWDDRIVER];
 
/*
 * WD interrupt handler
 */
static void
wd8003Enet_interrupt_handler (void)
{
  unsigned int tport;
  unsigned char status, status2;
 
  tport = wd_softc[0].port ;
 
  /*
   * Read status
   */
  inport_byte(tport+ISR, status);
  outport_byte(tport+IMR, 0x00);
 
  /*
   * Ring overwrite
   */
 
  if (status & MSK_OVW){
    outport_byte(tport+CMDR, MSK_STP + MSK_RD2);	/* stop 8390 */
    Wait_X_ms(2);
    outport_byte(tport+RBCR0, 0);			/* clear byte count */
    outport_byte(tport+RBCR1, 0);
    inport_byte(tport+ISR, status2);
    status |= (status2 & (MSK_PTX+MSK_TXE)) ;	/* TX status */
    outport_byte(tport+TCR, MSK_LOOP);		/* loopback mode */
    outport_byte(tport+CMDR, MSK_STA + MSK_RD2);	/* start */
    overrun = 1 ;
    if ((status & (MSK_PTX+MSK_TXE)) == 0)
	resend = 1;
  }
 
  /*
   * Frame received?
   */
  if (status & (MSK_PRX+MSK_RXE)) {
    outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
    wd_softc[0].rxInterrupts++;
    rtems_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);    
  }
 
}
 
static void nopOn(const rtems_irq_connect_data* notUsed)
{
  /*
   * code should be moved from wd8003Enet_initialize_hardware
   * to this location
   */
}
 
static int wdIsOn(const rtems_irq_connect_data* irq)
{
  return BSP_irq_enabled_at_i8259s (irq->name);
}
 
/*
 * Initialize the ethernet hardware
 */
static void
wd8003Enet_initialize_hardware (struct wd_softc *sc)
{
  int  i1, ultra;
  char cc1, cc2;
  unsigned char  temp;
  rtems_status_code st;
  unsigned int tport;
  unsigned char *hwaddr;
 
  tport = sc->port;
 
  /* address from board ROM */
  inport_byte(tport+0x04, temp);
  outport_byte(tport+0x04, temp & 0x7f);
 
  hwaddr = sc->arpcom.ac_enaddr;
  for (i1=cc2=0; i1<8; i1++) {
    inport_byte(tport + ADDROM + i1, cc1);
    cc2 += cc1;
    if (i1 < 6)
      hwaddr[i1] = cc1;
  }
 
  inport_byte(tport+0x04, temp); 
  outport_byte(tport+0x04, temp | 0x80);	/* alternate registers */
  outport_byte(tport+W83CREG, MSK_RESET);	/* reset board, set buffer */
  outport_byte(tport+W83CREG, 0);
  outport_byte(tport+W83CREG, MSK_ENASH + (int)((sc->bpar>>13)&0x3f));
 
  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
  cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
 
  inport_byte(tport+0x07, temp) ; 
 
  ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
  if (ultra)
    cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
  outport_byte(tport+DCR, cc1);
  outport_byte(tport+RBCR0, 0);
  outport_byte(tport+RBCR1, 0);
  outport_byte(tport+RCR, MSK_MON);	       	/* disable the rxer */
  outport_byte(tport+TCR, 0);			/* normal operation */
  outport_byte(tport+PSTOP, OUTPAGE);		/* init PSTOP */
  outport_byte(tport+PSTART, 0);	       	/* init PSTART */
  outport_byte(tport+BNRY, -1);			/* init BNRY */
  outport_byte(tport+ISR, -1);			/* clear IR's */
  outport_byte(tport+IMR, 0x15);	       	/* enable interrupt */
 
  outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
 
  for (i1=0; i1<6; i1++)			/* initial physical addr */
    outport_byte(tport+PAR+i1, hwaddr[i1]);
 
  for (i1=0; i1<MARsize; i1++)			/* clear multicast */
    outport_byte(tport+MAR+i1, 0);
  outport_byte(tport+CURR, 0);			/* init current packet */
 
  outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
  outport_byte(tport+CMDR, MSK_STA + MSK_RD2);	/* put 8390 on line */
  outport_byte(tport+RCR, MSK_AB);		/* MSK_AB accept broadcast */
 
  if (ultra) {
    inport_byte(tport+0x0c, temp);
    outport_byte(tport+0x0c, temp | 0x80);
    outport_byte(tport+0x05, 0x80);
    outport_byte(tport+0x06, 0x01);
  }
 
  /*
   * Set up interrupts
   */
  sc->irqInfo.hdl = wd8003Enet_interrupt_handler;
  sc->irqInfo.on  = nopOn;
  sc->irqInfo.off = nopOn;
  sc->irqInfo.isOn = wdIsOn;
 
  st = BSP_install_rtems_irq_handler (&sc->irqInfo);
  if (!st)
    rtems_panic ("Can't attach WD interrupt handler for irq %d\n",
		  sc->irqInfo.name);
}
 
static void
wd_rxDaemon (void *arg)
{
  unsigned int tport;
  struct ether_header *eh;
  struct wd_softc *dp = (struct wd_softc *)&wd_softc[0];
  struct ifnet *ifp = &dp->arpcom.ac_if;
  struct mbuf *m;
  unsigned int i2;
  unsigned int len;
  volatile unsigned char start, next, current;
  char *shp, *temp;
  rtems_event_set events;
 
  tport = wd_softc[0].port ;
 
  for (;;){
 
 
    rtems_bsdnet_event_receive (INTERRUPT_EVENT,
				RTEMS_WAIT|RTEMS_EVENT_ANY,
				RTEMS_NO_TIMEOUT,
				&events);
 
    for (;;){
      inport_byte(tport+BNRY, start);
 
      outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
      inport_byte(tport+CURR, current);
      outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2); 
 
      start += 1;
      if (start >= OUTPAGE){
	start = 0;
      }
 
      if (current == start)
	break;
 
      shp = dp->base + 1 + (SHAPAGE * start);
      next = *shp++;
      len = *((short *)shp)++ - 4;
 
      if (next >= OUTPAGE){
	next = 0;
      }
 
      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);
 
      if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
	memcpy(temp, shp, i2);
	len -= i2;
	temp += i2;
	shp = dp->base;
      }
      memcpy(temp, shp, len);
 
      eh = mtod (m, struct ether_header *);
      m->m_data += sizeof(struct ether_header);
      ether_input (ifp, eh, m);
 
      outport_byte(tport+BNRY, next-1);
    }
 
  /*
   * Ring overwrite
   */
    if (overrun){     
      outport_byte(tport+ISR, MSK_OVW);		/* reset IR */
      outport_byte(tport+TCR, 0);		/* out of loopback */
      if (resend  == 1)
	outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);	/* resend */
      resend = 0;
      overrun = 0;
    }
 
    outport_byte(tport+IMR, 0x15);  /* re-enable IT rx */
  }	
}
 
static void
sendpacket (struct ifnet *ifp, struct mbuf *m)
{
	struct wd_softc *dp = ifp->if_softc;
	struct mbuf *n;
	unsigned int len, tport;
	char *shp, txReady;
 
	tport = dp->port;
 
  /*
   * Waiting for Transmitter ready
   */	
  inport_byte(tport+CMDR, txReady); 
  while(txReady & MSK_TXP)
    inport_byte(tport+CMDR, txReady); 
 
  len = 0;
  shp = dp->base + (SHAPAGE * OUTPAGE);
 
  n = m;
 
  for (;;){
    len += m->m_len;
    memcpy(shp, (char *)m->m_data, m->m_len);
    shp += m->m_len ;
    if ((m = m->m_next) == NULL)
      break;
  }
 
  m_freem(n);
 
  if (len < ET_MINLEN) len = ET_MINLEN;
  outport_byte(tport+TBCR0, len);
  outport_byte(tport+TBCR1, (len >> 8) );
  outport_byte(tport+TPSR, OUTPAGE);
  outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
}
 
/*
 * Driver transmit daemon
 */
void
wd_txDaemon (void *arg)
{
	struct wd_softc *sc = (struct wd_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;
	}
}
 
/*
 * Send packet (caller provides header).
 */
static void
wd_start (struct ifnet *ifp)
{
	struct wd_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
wd_init (void *arg)
{
  struct wd_softc *sc = arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
 
  if (sc->txDaemonTid == 0) {
 
    /*
     * Set up WD hardware
     */
    wd8003Enet_initialize_hardware (sc);
 
    /*
     * Start driver tasks
     */
    sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, wd_txDaemon, sc);
    sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, wd_rxDaemon, sc);
  }
 
  /*
   * Tell the world that we're running.
   */
  ifp->if_flags |= IFF_RUNNING;
 
}
 
/*
 * Stop the device
 */
static void
wd_stop (struct wd_softc *sc)
{
  unsigned int tport;
  unsigned char temp;
  struct ifnet *ifp = &sc->arpcom.ac_if;
 
  ifp->if_flags &= ~IFF_RUNNING;
 
  /*
   * Stop the transmitter
   */
  tport=wd_softc[0].port ;
  inport_byte(tport+0x04,temp);
  outport_byte(tport+0x04, temp & 0x7f);
  outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
 
}
 
 
/*
 * Show interface statistics
 */
static void
wd_stats (struct wd_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
wd_ioctl (struct ifnet *ifp, int command, caddr_t data)
{
	struct wd_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:
			wd_stop (sc);
			break;
 
		case IFF_UP:
			wd_init (sc);
			break;
 
		case IFF_UP | IFF_RUNNING:
			wd_stop (sc);
			wd_init (sc);
			break;
 
		default:
			break;
		}
		break;
 
	case SIO_RTEMS_SHOW_STATS:
		wd_stats (sc);
		break;
 
	/*
	 * FIXME: All sorts of multicast commands need to be added here!
	 */
	default:
		error = EINVAL;
		break;
	}
	return error;
}
 
/*
 * Attach an WD driver to the system
 */
int
rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config)
{
	struct wd_softc *sc;
	struct ifnet *ifp;
	int mtu;
	int i;
 
	/*
	 * Find a free driver
	 */
	for (i = 0 ; i < NWDDRIVER ; i++) {
		sc = &wd_softc[i];
		ifp = &sc->arpcom.ac_if;
		if (ifp->if_softc == NULL)
			break;
	}
	if (i >= NWDDRIVER) {
		printf ("Too many WD drivers.\n");
		return 0;
	}
 
	/*
	 * Process options
	 */
	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;
 
 
	if (config->irno)
		sc->irqInfo.name = config->irno;
	else
		sc->irqInfo.name = 5;
 
	if (config->port)
		sc->port = config->port;
	else
		sc->port = 0x240;
 
	if (config->bpar) {
		sc->bpar = config->bpar;
		sc->base = (unsigned char*) config->bpar;
	}
	else {
		sc->bpar = 0xD0000;
		sc->base = (unsigned char*) 0xD0000;
	}
 
	sc->acceptBroadcast = !config->ignore_broadcast;
 
	/*
	 * Set up network interface values
	 */
	ifp->if_softc = sc;
	ifp->if_unit = i + 1;
	ifp->if_name = "wd";
	ifp->if_mtu = mtu;
	ifp->if_init = wd_init;
	ifp->if_ioctl = wd_ioctl;
	ifp->if_start = wd_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;
 
	/*
	 * init some variables
	 */
	overrun = 0;
	resend = 0;
 
 	/*
	 * Attach the interface
	 */
	if_attach (ifp);
	ether_ifattach (ifp);
	return 1;
};
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.