/*
 *  This file contains definitions for LatticeMico32 TSMAC (Tri-Speed MAC)
 *
 *  COPYRIGHT (c) 1989-1999.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 *  Jukka Pietarinen <jukka.pietarinen@mrf.fi>, 2008,
 *  Micro-Research Finland Oy
 */

#include <machine/rtems-bsd-kernel-space.h>

#include <rtems.h>
#include <bsp.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <rtems/error.h>
#include <rtems/bspIo.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 "../include/system_conf.h"
#include "tsmac.h"
#include "dp83848phy.h"

struct tsmac_softc {
  struct arpcom arpcom;
  void *ioaddr;
  rtems_id rxDaemonTid;
  rtems_id txDaemonTid;

  /*
   * Statistics
   */
  int rxInterrupts;
  int rxPktIgnore;
  int rxLenCheckError;
  int rxLongFrame;
  int rxShortFrame;
  int rxIPGViolation;
  int rxCRCError;
  int rxOKPackets;
  int rxControlFrame;
  int rxPauseFrame;
  int rxMulticast;
  int rxBroadcast;
  int rxVLANTag;
  int rxPreShrink;
  int rxDribNib;
  int rxUnsupOPCD;
  int rxByteCnt;
  int rxFifoFull;

  int txInterrupts;
  int txUnicast;
  int txPauseFrame;
  int txMulticast;
  int txBroadcast;
  int txVLANTag;
  int txBadFCS;
  int txJumboCnt;
  int txByteCnt;
  int txLostCarrier;
  int txFifoFull;
};

/*
 * Macros to access tsmac wrapper registers.
 */

static inline uint32_t tsmacread(unsigned int reg)
{
  return *((uint32_t *)(TS_MAC_CORE_BASE_ADDRESS + reg));
}

static inline void tsmacwrite(unsigned int reg, uint32_t value)
{
  *((uint32_t *)(TS_MAC_CORE_BASE_ADDRESS + reg)) = value;
}

/*
 * tsmac is a wishbone to MAC wrapper.
 * The macros below access to MAC registers.
 */

static inline uint16_t tsmacregread(unsigned int reg)
{
  tsmacwrite(LM32_TSMAC_MAC_REGS_ADDR_RW, reg);
  return *((uint16_t *)(TS_MAC_CORE_BASE_ADDRESS + LM32_TSMAC_MAC_REGS_DATA + 2));
}

static inline void tsmacregwrite(unsigned int reg, uint16_t value)
{
  *((uint16_t *)(TS_MAC_CORE_BASE_ADDRESS + LM32_TSMAC_MAC_REGS_DATA + 2)) = value;
  tsmacwrite(LM32_TSMAC_MAC_REGS_ADDR_RW, REGS_ADDR_WRITE | reg);
}

/*
#define DEBUG 1
*/

/* We support one interface */

#define TSMAC_NUM  1
#define TSMAC_NAME "TSMAC"
#define TSMAC_MAC0 0x00
#define TSMAC_MAC1 0x0E
#define TSMAC_MAC2 0xB2
#define TSMAC_MAC3 0x00
#define TSMAC_MAC4 0x00
#define TSMAC_MAC5 0x01

/*
 *  The interrupt vector number associated with the tsmac device
 *  driver.
 */

#define TSMAC_VECTOR    ( TS_MAC_CORE_IRQ )
#define TSMAC_IRQMASK   ( 1 << TSMAC_VECTOR )

rtems_isr tsmac_interrupt_handler(rtems_vector_number vector);

extern rtems_isr_entry set_vector(rtems_isr_entry handler,
				 rtems_vector_number vector, int type);

/*
 * Macros to access PHY registers through the (G)MII
 */

uint16_t tsmacphyread(unsigned int reg)
{
  tsmacregwrite(LM32_TSMAC_GMII_MNG_CTL_BYTE0,
		((DEFAULT_PHY_ADDRESS & GMII_MNG_CTL_PHY_ADD_MASK) <<
		 GMII_MNG_CTL_PHY_ADD_SHIFT) |
		((reg & GMII_MNG_CTL_REG_ADD_MASK) <<
		 GMII_MNG_CTL_REG_ADD_SHIFT) |
		GMII_MNG_CTL_READ_PHYREG);

  /* Wait for management interface to be ready */
  while(!(tsmacregread(LM32_TSMAC_GMII_MNG_CTL_BYTE0) & GMII_MNG_CTL_CMD_FIN));

  return tsmacregread(LM32_TSMAC_GMII_MNG_DAT_BYTE0);
}

void tsmacphywrite(unsigned int reg, uint16_t value)
{
  tsmacregwrite(LM32_TSMAC_GMII_MNG_DAT_BYTE0, value);
  tsmacregwrite(LM32_TSMAC_GMII_MNG_CTL_BYTE0,
		((DEFAULT_PHY_ADDRESS & GMII_MNG_CTL_PHY_ADD_MASK) <<
		 GMII_MNG_CTL_PHY_ADD_SHIFT) |
		((reg & GMII_MNG_CTL_REG_ADD_MASK) <<
		 GMII_MNG_CTL_REG_ADD_SHIFT) |
		GMII_MNG_CTL_WRITE_PHYREG);

  /* Wait for management interface to be ready */
  while(!(tsmacregread(LM32_TSMAC_GMII_MNG_CTL_BYTE0) & GMII_MNG_CTL_CMD_FIN));
}

/*
 * Event definitions
 */
#define INTERRUPT_EVENT      RTEMS_EVENT_1
#define START_TRANSMIT_EVENT RTEMS_EVENT_2

static struct tsmac_softc tsmac_softc[TSMAC_NUM];

#ifdef CPU_U32_FIX

/*
 * Routine to align the received packet so that the ip header
 * is on a 32-bit boundary. Necessary for cpu's that do not
 * allow unaligned loads and stores and when the 32-bit DMA
 * mode is used.
 *
 * Transfers are done on word basis to avoid possibly slow byte
 * and half-word writes.
 *
 * Copied over from sonic.c driver
 */

void ipalign(struct mbuf *m)
{
  unsigned int *first, *last, data;
  unsigned int tmp = 0;

  if ((((int) m->m_data) & 2) && (m->m_len)) {
    last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
    first = (unsigned int *) (((int) m->m_data) & ~3);
    tmp = *first << 16;
    first++;
    do {
      data = *first;
      *first = tmp | (data >> 16);
      tmp = data << 16;
      first++;
    } while (first <= last);

    m->m_data = (caddr_t)(((int) m->m_data) + 2);
  }
}
#endif

/*
 * Receive task
 */
static void tsmac_rxDaemon(void *arg)
{
  struct tsmac_softc *tsmac = (struct tsmac_softc *) arg;
  struct ifnet *ifp = &tsmac->arpcom.ac_if;
  rtems_event_set events;
  int rxq, count, len, data;

#ifdef DEBUG
  printk(TSMAC_NAME ": tsmac_rxDaemon\n");
#endif

  for(;;)
    {
      rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
				  RTEMS_WAIT | RTEMS_EVENT_ANY,
				  RTEMS_NO_TIMEOUT,
				  &events);

#ifdef DEBUG
      printk(TSMAC_NAME ": tsmac_rxDaemon wakeup\n");
#endif

      for (;;)
	{
	  struct mbuf* m;
	  struct ether_header* eh;
	  uint32_t *buf;

	  /* Get number of RX frames in RX FIFO */
	  rxq = tsmacread(LM32_TSMAC_RX_FRAMES_CNT);

	  if (rxq == 0)
	    break;

	  /* Get length of frame */
	  len = tsmacread(LM32_TSMAC_RX_LEN_FIFO);
#ifdef DEBUG
	  printk(TSMAC_NAME ": Frames %d, len 0x%04x (%d)\n",
		 rxq, len, len);
#endif

	  /*
	   * Get memory for packet
	   */
	  MGETHDR(m, M_WAIT, MT_DATA);
	  MCLGET(m, M_WAIT);

	  m->m_pkthdr.rcvif = ifp;

	  buf = (uint32_t *) mtod(m, uint32_t*);
	  for (count = 0; count < len; count += 4)
	    {
	      data = tsmacread(LM32_TSMAC_RX_DATA_FIFO);
	      *buf++ = data;
#ifdef DEBUG
	      printk("%08x ", data);
#endif
	    }
#ifdef DEBUG
	  printk("\n");
#endif

	  m->m_len = m->m_pkthdr.len =
	    len - sizeof(uint32_t) - sizeof(struct ether_header);
	  eh = mtod(m, struct ether_header*);
	  m->m_data += sizeof(struct ether_header);

#ifdef CPU_U32_FIX
      ipalign(m);	/* Align packet on 32-bit boundary */
#endif

	  /* Notify the ip stack that there is a new packet */
	  ether_input(ifp, eh, m);

	  /*
	   * Release RX frame
	   */
	}
    }
}

static unsigned char tsmac_txbuf[2048];

static void tsmac_sendpacket(struct ifnet *ifp, struct mbuf *m)
{
  struct mbuf *nm = m;
  int len = 0, i;
  uint32_t *buf;

#ifdef DEBUG
  printk(TSMAC_NAME ": tsmac_sendpacket\n");
#endif

  do
    {
#ifdef DEBUG
      printk("mbuf: 0x%08x len %03x: ", nm->m_data, nm->m_len);
      for (i = 0; i < nm->m_len; i++)
	{
	  printk("%02x", mtod(nm, unsigned char*)[i]);
	  if (i & 1)
	    printk(" ");
	}
      printk("\n");
#endif

      if (nm->m_len > 0)
	{
	  memcpy(&tsmac_txbuf[len], (char *)nm->m_data, nm->m_len);
	  len += nm->m_len;
	}
    }
  while ((nm = nm->m_next) != 0);

  buf = (uint32_t *) tsmac_txbuf;
  for (i = 0; i < len; i += 4)
    {
#ifdef DEBUG
      printk("%08x", *buf);
#endif
      tsmacwrite(LM32_TSMAC_TX_DATA_FIFO, *buf++);
    }
#ifdef DEBUG
  printk("\n");
#endif

  /*
   * Enqueue TX frame
   */
  tsmacwrite(LM32_TSMAC_TX_LEN_FIFO, len);
}

/*
 * Transmit task
 */
static void tsmac_txDaemon(void *arg)
{
  struct tsmac_softc *tsmac = (struct tsmac_softc *) arg;
  struct ifnet *ifp = &tsmac->arpcom.ac_if;
  struct mbuf *m;
  rtems_event_set events;
  int txq;

#ifdef DEBUG
  printk(TSMAC_NAME ": tsmac_txDaemon\n");
#endif

  for (;;)
    {
      /*
       * Wait for packet
       */
      rtems_bsdnet_event_receive (START_TRANSMIT_EVENT | INTERRUPT_EVENT,
				  RTEMS_EVENT_ANY | RTEMS_WAIT,
				  RTEMS_NO_TIMEOUT, &events);
#ifdef DEBUG
      printk(TSMAC_NAME ": tsmac_txDaemon event\n");
#endif
      for (;;)
	{
	  /*
	   * Here we should read amount of transmit memory available
	   */

	  txq = 2048;

	  if (txq < ifp->if_mtu)
	    {
	      /*
	       * Here we need to enable transmit done IRQ
	       */
#ifdef DEBUG
	      printk(TSMAC_NAME ": TXMA %d < MTU + CW%d\n", txq,
		     ifp->if_mtu);
#endif
	      break;
	    }

	  /*
	   * Get the next mbuf chain to transmit.
	   */
	  IF_DEQUEUE(&ifp->if_snd, m);
#ifdef DEBUG
	  printk(TSMAC_NAME ": mbuf %08x\n", (int) m);
#endif
	  if (!m)
	    break;
	  tsmac_sendpacket(ifp, m);

	  m_freem(m);
	}
      ifp->if_flags &= ~IFF_OACTIVE;
    }
}

/*
 * Initialize TSMAC hardware
 */
void tsmac_init_hardware(struct tsmac_softc *tsmac)
{
  unsigned char *mac_addr;
  int version_id, phyid, stat;

#ifdef DEBUG
  printk(TSMAC_NAME ": tsmac_init_hardware\n");
#endif

  version_id = tsmacread(LM32_TSMAC_VERID);
#ifdef DEBUG
  printk(TSMAC_NAME ": Version ID %08x\n", version_id);
#endif

#ifdef DEBUG
  printk(TSMAC_NAME ": MAC MODE %04x\n", tsmacregread(LM32_TSMAC_MODE_BYTE0));
  printk(TSMAC_NAME ": MAC TX_RX_CTL %04x\n", tsmacregread(LM32_TSMAC_TX_RX_CTL_BYTE0));
  printk(TSMAC_NAME ": MAC MAX_PKT_SIZE %04x\n", tsmacregread(LM32_TSMAC_MAX_PKT_SIZE_BYTE0));
  printk(TSMAC_NAME ": MAC IPG_VAL %04x\n", tsmacregread(LM32_TSMAC_IPG_VAL_BYTE0));
  printk(TSMAC_NAME ": MAC MAC_ADDR0 %04x\n",
	 tsmacregread(LM32_TSMAC_MAC_ADDR_0_BYTE0));
  printk(TSMAC_NAME ": MAC MAC_ADDR1 %04x\n",
	 tsmacregread(LM32_TSMAC_MAC_ADDR_1_BYTE0));
  printk(TSMAC_NAME ": MAC MAC_ADDR2 %04x\n",
	 tsmacregread(LM32_TSMAC_MAC_ADDR_2_BYTE0));
  printk(TSMAC_NAME ": MAC TX_RX_STS %04x\n",
	 tsmacregread(LM32_TSMAC_TX_RX_STS_BYTE0));
#endif

  /*
   * Set our physical address
   */
  mac_addr = tsmac->arpcom.ac_enaddr;
  tsmacregwrite(LM32_TSMAC_MAC_ADDR_0_BYTE0, (mac_addr[0] << 8) | mac_addr[1]);
  tsmacregwrite(LM32_TSMAC_MAC_ADDR_1_BYTE0, (mac_addr[2] << 8) | mac_addr[3]);
  tsmacregwrite(LM32_TSMAC_MAC_ADDR_2_BYTE0, (mac_addr[4] << 8) | mac_addr[5]);

#ifdef DEBUG
  printk(TSMAC_NAME ": After setting MAC address.\n");
  printk(TSMAC_NAME ": MAC MAC_ADDR0 %04x\n",
	 tsmacregread(LM32_TSMAC_MAC_ADDR_0_BYTE0));
  printk(TSMAC_NAME ": MAC MAC_ADDR1 %04x\n",
	 tsmacregread(LM32_TSMAC_MAC_ADDR_1_BYTE0));
  printk(TSMAC_NAME ": MAC MAC_ADDR2 %04x\n",
	 tsmacregread(LM32_TSMAC_MAC_ADDR_2_BYTE0));
#endif

  /*
   * Configure PHY
   */

  phyid = tsmacphyread(PHY_PHYIDR1);
#ifdef DEBUG
  printk(TSMAC_NAME ": PHYIDR1 %08x\n", phyid);
#endif
  phyid = tsmacphyread(PHY_PHYIDR2);
#ifdef DEBUG
  printk(TSMAC_NAME ": PHYIDR2 %08x\n", phyid);
#endif

#ifdef TSMAC_FORCE_10BASET
  /* Force 10baseT mode, no AN, full duplex */
  tsmacphywrite(PHY_BMCR, PHY_BMCR_DUPLEX_MODE);
  stat = tsmacphyread(PHY_BMCR);
#ifdef DEBUG
  printk(TSMAC_NAME ": PHY BMCR %04x, wrote %04x\n", stat,
	 PHY_BMCR_DUPLEX_MODE);
#endif
  stat = tsmacphyread(PHY_BMSR);
#ifdef DEBUG
  printk(TSMAC_NAME ": PHY BMSR %04x\n", stat);
#endif
  /* Support for 10baseT modes only */
  tsmacphywrite(PHY_ANAR, PHY_ANAR_10_FD | PHY_ANAR_10 | PHY_ANAR_SEL_DEF);
  stat = tsmacphyread(PHY_ANAR);
#ifdef DEBUG
  printk(TSMAC_NAME ": PHY ANAR %04x, wrote %04x\n", stat,
	 PHY_ANAR_10_FD | PHY_ANAR_10 | PHY_ANAR_SEL_DEF);
#endif
#endif /* TSMAC_FORCE_10BASET */
  stat = tsmacphyread(PHY_PHYSTS);
#ifdef DEBUG
  printk(TSMAC_NAME ": PHY PHYSTS %04x\n", stat);
#endif

  /* Enable receive and transmit interrupts */
  tsmacwrite(LM32_TSMAC_INTR_ENB, INTR_ENB |
	     INTR_RX_SMRY | INTR_TX_SMRY |
	     INTR_RX_PKT_RDY | INTR_TX_PKT_SENT);
}

/*
 * Initialize and start the device
 */
void tsmac_init(void *arg)
{
  struct tsmac_softc *tsmac = &tsmac_softc[0];
  struct ifnet *ifp = &tsmac->arpcom.ac_if;

#ifdef DEBUG
  printk(TSMAC_NAME ": tsmac_init, tsmac->txDaemonTid = 0x%x\n",
	 tsmac->txDaemonTid);
#endif

  if (tsmac->txDaemonTid == 0)
    {
      /*
       * Initialize hardware
       */
      tsmac_init_hardware(tsmac);

      /*
       * Start driver tasks
       */
      tsmac->txDaemonTid = rtems_bsdnet_newproc ("TSMACtx", 4096,
					       tsmac_txDaemon, tsmac);
      tsmac->rxDaemonTid = rtems_bsdnet_newproc ("TSMACrx", 4096,
					       tsmac_rxDaemon, tsmac);
      /*
       * Setup interrupt handler
       */
      set_vector( tsmac_interrupt_handler, TSMAC_VECTOR, 1 );

      /* Interrupt line for TSMAC */
      lm32_interrupt_unmask(TSMAC_IRQMASK);
   }

  ifp->if_flags |= IFF_RUNNING;

  /*
   * Receive broadcast
   */

  tsmacregwrite(LM32_TSMAC_TX_RX_CTL_BYTE0, TX_RX_CTL_RECEIVE_BRDCST |
		TX_RX_CTL_RECEIVE_PAUSE);

  /*
   * Enable transmitter
   * Flow control enable
   * Enable receiver
   */

  tsmacregwrite(LM32_TSMAC_MODE_BYTE0, MODE_TX_EN | MODE_RX_EN | MODE_FC_EN);

  /*
   * Wake up receive task to receive packets in queue
   */
  rtems_bsdnet_event_send(tsmac->rxDaemonTid, INTERRUPT_EVENT);
}

void tsmac_stop(struct ifnet *ifp)
{
  /*
   * Mask tsmac interrupts
   */
  lm32_interrupt_mask(TSMAC_IRQMASK);

  ifp->if_flags &= ~IFF_RUNNING;

  /*
   * Disable transmitter and receiver
   */
  tsmacregwrite(LM32_TSMAC_MODE_BYTE0, 0);
}

/*
 * Send packet
 */
void tsmac_start(struct ifnet *ifp)
{
  struct tsmac_softc *tsmac = ifp->if_softc;

  rtems_bsdnet_event_send (tsmac->txDaemonTid, START_TRANSMIT_EVENT);
  ifp->if_flags |= IFF_OACTIVE;
}

void tsmac_stats(struct tsmac_softc *tsmac)
{
  /*
   * Update counters from TSMAC MIB counters
   */

  tsmac->rxPktIgnore = tsmacread(LM32_TSMAC_RX_PKT_IGNR_CNT);
  tsmac->rxLenCheckError = tsmacread(LM32_TSMAC_RX_LEN_CHK_ERR_CNT);
  tsmac->rxLongFrame = tsmacread(LM32_TSMAC_RX_LNG_FRM_CNT);
  tsmac->rxShortFrame = tsmacread(LM32_TSMAC_RX_SHRT_FRM_CNT);
  tsmac->rxIPGViolation = tsmacread(LM32_TSMAC_RX_IPG_VIOL_CNT);
  tsmac->rxCRCError = tsmacread(LM32_TSMAC_RX_CRC_ERR_CNT);
  tsmac->rxOKPackets = tsmacread(LM32_TSMAC_RX_OK_PKT_CNT);
  tsmac->rxControlFrame = tsmacread(LM32_TSMAC_RX_CTL_FRM_CNT);
  tsmac->rxPauseFrame = tsmacread(LM32_TSMAC_RX_PAUSE_FRM_CNT);
  tsmac->rxMulticast = tsmacread(LM32_TSMAC_RX_MULTICAST_CNT);
  tsmac->rxBroadcast = tsmacread(LM32_TSMAC_RX_BRDCAST_CNT);
  tsmac->rxVLANTag = tsmacread(LM32_TSMAC_RX_VLAN_TAG_CNT);
  tsmac->rxPreShrink = tsmacread(LM32_TSMAC_RX_PRE_SHRINK_CNT);
  tsmac->rxDribNib = tsmacread(LM32_TSMAC_RX_DRIB_NIB_CNT);
  tsmac->rxUnsupOPCD = tsmacread(LM32_TSMAC_RX_UNSUP_OPCD_CNT);
  tsmac->rxByteCnt = tsmacread(LM32_TSMAC_RX_BYTE_CNT);

  tsmac->txUnicast = tsmacread(LM32_TSMAC_TX_UNICAST_CNT);
  tsmac->txPauseFrame = tsmacread(LM32_TSMAC_TX_PAUSE_FRM_CNT);
  tsmac->txMulticast = tsmacread(LM32_TSMAC_TX_MULTICAST_CNT);
  tsmac->txBroadcast = tsmacread(LM32_TSMAC_TX_BRDCAST_CNT);
  tsmac->txVLANTag = tsmacread(LM32_TSMAC_TX_VLAN_TAG_CNT);
  tsmac->txBadFCS = tsmacread(LM32_TSMAC_TX_BAD_FCS_CNT);
  tsmac->txJumboCnt = tsmacread(LM32_TSMAC_TX_JUMBO_CNT);
  tsmac->txByteCnt = tsmacread(LM32_TSMAC_TX_BYTE_CNT);

  printk("RX Interrupts:    %8d", tsmac->rxInterrupts);
  printk(" RX Len Chk Error: %8d", tsmac->rxLenCheckError);
  printk(" RX Long Frame:    %8d\n", tsmac->rxLongFrame);
  printk("RX Short Frame:   %8d", tsmac->rxShortFrame);
  printk(" RX IPG Violation: %8d", tsmac->rxIPGViolation);
  printk(" RX CRC Errors:    %8d\n", tsmac->rxCRCError);
  printk("RX OK Packets:    %8d", tsmac->rxOKPackets);
  printk(" RX Control Frame: %8d", tsmac->rxControlFrame);
  printk(" RX Pause Frame:   %8d\n", tsmac->rxPauseFrame);
  printk("RX Multicast:     %8d", tsmac->rxMulticast);
  printk(" RX Broadcast:     %8d", tsmac->rxBroadcast);
  printk(" RX VLAN Tag:      %8d\n", tsmac->rxVLANTag);
  printk("RX Pre Shrink:    %8d", tsmac->rxPreShrink);
  printk(" RX Dribb. Nibble: %8d", tsmac->rxDribNib);
  printk(" RX Unsupp. OPCD:  %8d\n", tsmac->rxUnsupOPCD);
  printk("RX Byte Count:    %8d", tsmac->rxByteCnt);
  printk(" RX FIFO Full:     %8d\n", tsmac->rxFifoFull);

  printk("TX Interrupts:    %8d", tsmac->txInterrupts);
  printk(" TX Unicast:       %8d", tsmac->txUnicast);
  printk(" TX Pause Frame:   %8d\n", tsmac->txPauseFrame);
  printk("TX Multicast:     %8d", tsmac->txMulticast);
  printk(" TX Broadcast:     %8d", tsmac->txBroadcast);
  printk(" TX VLAN Tag:      %8d\n", tsmac->txVLANTag);
  printk("TX Bad FSC:       %8d", tsmac->txBadFCS);
  printk(" TX Jumbo Frame:   %8d", tsmac->txJumboCnt);
  printk(" TX Byte Count:    %8d\n", tsmac->txByteCnt);
  printk("TX FIFO Full:     %8d\n", tsmac->txFifoFull);
}

/*
 * TSMAC ioctl handler
 */

int tsmac_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
{
  struct tsmac_softc *tsmac = 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:
      tsmac_stop ((struct ifnet *) tsmac);
      break;

    case IFF_UP:
      tsmac_init ((struct ifnet *) tsmac);
      break;

    case IFF_UP | IFF_RUNNING:
      tsmac_stop ((struct ifnet *) tsmac);
      tsmac_init ((struct ifnet *) tsmac);
      break;

    default:
      break;
    }
    break;

  case SIO_RTEMS_SHOW_STATS:
    tsmac_stats (tsmac);
    break;

  default:
    error = EINVAL;
    break;
  }
  return error;
}

/*
 * Attach a TSMAC driver
 */
int rtems_tsmac_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
{
  struct tsmac_softc *tsmac;
  struct ifnet *ifp;
  int mtu, i;
  int unit;
  char *unitName;

  if ((unit = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0)
    {
      printk(TSMAC_NAME ": Driver name parsing failed.\n");
      return 0;
    }

  if ((unit < 0) || (unit >= TSMAC_NUM))
    {
      printk(TSMAC_NAME ": Bad unit number %d.\n", unit);
      return 0;
    }

  tsmac = &tsmac_softc[unit];

  ifp = &tsmac->arpcom.ac_if;
  if (ifp->if_softc != NULL)
    {
      printk(TSMAC_NAME ": Driver already in use.\n");
      return 0;
    }

  /* Base address for TSMAC */
  if (config->bpar == 0)
    {
      printk(TSMAC_NAME ": Using default base address 0x%08x.\n", TS_MAC_CORE_BASE_ADDRESS);
      config->bpar = TS_MAC_CORE_BASE_ADDRESS;
    }
  tsmac->ioaddr = config->bpar;

  /* Hardware address for TSMAC */
  if (config->hardware_address == 0)
    {
      printk(TSMAC_NAME ": Using default hardware address.\n");
      tsmac->arpcom.ac_enaddr[0] = TSMAC_MAC0;
      tsmac->arpcom.ac_enaddr[1] = TSMAC_MAC1;
      tsmac->arpcom.ac_enaddr[2] = TSMAC_MAC2;
      tsmac->arpcom.ac_enaddr[3] = TSMAC_MAC3;
      tsmac->arpcom.ac_enaddr[4] = TSMAC_MAC4;
      tsmac->arpcom.ac_enaddr[5] = TSMAC_MAC5;
    }
  else
    memcpy(tsmac->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);

  printk(TSMAC_NAME ": MAC address ");
  for (i = 0; i < ETHER_ADDR_LEN; i++)
    {
      printk("%02x", tsmac->arpcom.ac_enaddr[i]);
      if (i != ETHER_ADDR_LEN-1)
	printk(":");
      else
	printk("\n");
    }

  if (config->mtu)
    mtu = config->mtu;
  else
    mtu = ETHERMTU;

  /*
   * Set up network interface values
   */
  ifp->if_softc = tsmac;
  ifp->if_unit = unit;
  ifp->if_name = unitName;
  ifp->if_mtu = mtu;
  ifp->if_init = tsmac_init;
  ifp->if_ioctl = tsmac_ioctl;
  ifp->if_start = tsmac_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;

  if_attach(ifp);
  ether_ifattach(ifp);

  return 1;
}

rtems_isr tsmac_interrupt_handler(rtems_vector_number vector)
{
  struct tsmac_softc *tsmac = &tsmac_softc[0];
  uint32_t irq_stat, rx_stat, tx_stat;

  irq_stat = tsmacread(LM32_TSMAC_INTR_SRC);
  if (irq_stat & INTR_RX_PKT_RDY)
    {
      tsmac->rxInterrupts++;
      rtems_bsdnet_event_send(tsmac->rxDaemonTid, INTERRUPT_EVENT);
    }

  if (irq_stat & INTR_TX_PKT_SENT)
    {
      tsmac->txInterrupts++;
      rtems_bsdnet_event_send(tsmac->txDaemonTid, INTERRUPT_EVENT);
    }

  rx_stat = tsmacread(LM32_TSMAC_RX_STATUS);
  if (rx_stat & STAT_RX_FIFO_FULL)
    tsmac->rxFifoFull++;

  tx_stat = tsmacread(LM32_TSMAC_TX_STATUS);
  if (tx_stat & STAT_TX_FIFO_FULL)
    tsmac->txFifoFull++;

  lm32_interrupt_ack(TSMAC_IRQMASK);
}

