[rtems commit] bsp/genmcf548x: Fix network FIFO error handling

Sebastian Huber sebh at rtems.org
Mon Dec 16 14:13:16 UTC 2013


Module:    rtems
Branch:    master
Commit:    a19b9157e98e7925e763b1a0fa42000f7dc29218
Changeset: http://git.rtems.org/rtems/commit/?id=a19b9157e98e7925e763b1a0fa42000f7dc29218

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Wed Dec 11 11:35:10 2013 +0100

bsp/genmcf548x: Fix network FIFO error handling

Use a buffer handling similar to the MPC5200B FEC driver to cope with
FIFO errors.

---

 c/src/lib/libbsp/m68k/genmcf548x/network/network.c | 1015 +++++++++-----------
 1 files changed, 439 insertions(+), 576 deletions(-)

diff --git a/c/src/lib/libbsp/m68k/genmcf548x/network/network.c b/c/src/lib/libbsp/m68k/genmcf548x/network/network.c
index bd3692f..fd6098c 100644
--- a/c/src/lib/libbsp/m68k/genmcf548x/network/network.c
+++ b/c/src/lib/libbsp/m68k/genmcf548x/network/network.c
@@ -36,6 +36,7 @@
  *  COPYRIGHT (c) 2009, IMD
  *
  */
+
 #include <rtems.h>
 #include <rtems/error.h>
 #include <rtems/rtems_bsdnet.h>
@@ -59,34 +60,18 @@
 #include <mcf548x/MCD_dma.h>
 #include <mcf548x/mcdma_glue.h>
 
-#define ETH_PROMISCOUS_MODE 1 /* FIXME: remove me */
-
 /*
  * Number of interfaces supported by this driver
  */
 #define NIFACES 2
 
 #define FEC_WATCHDOG_TIMEOUT 5 /* check media every 5 seconds */
-/*
- * buffer descriptor handling
- */
-
-#define SET_BD_STATUS(bd, stat)	{		\
-    (bd)->statCtrl = stat;			\
-}
-#define SET_BD_LENGTH(bd, len) {		\
-    (bd)->length = len;				\
-}
-#define SET_BD_BUFFER(bd, buf) {		\
-    (bd)->dataPointer= (uint32_t)(buf);		\
-}
-#define GET_BD_STATUS(bd)		((bd)->statCtrl)
-#define GET_BD_LENGTH(bd)		((bd)->length)
-#define GET_BD_BUFFER(bd)		((void *)((bd)->dataPointer))
 
 #define DMA_BD_RX_NUM	32 /* Number of receive buffer descriptors	*/
 #define DMA_BD_TX_NUM	32 /* Number of transmit buffer descriptors	*/
 
+#define FEC_EVENT RTEMS_EVENT_0
+
 /*
  * internal SRAM
  * Layout:
@@ -133,9 +118,6 @@ extern char _SysSramBase[];
 					 /(MCF548X_FEC1_IRQ_VECTOR	\
 					   -MCF548X_FEC0_IRQ_VECTOR))
 
-#define FEC_RECV_TASK_NO        4
-#define FEC_XMIT_TASK_NO        5
-
 #define MCDMA_FEC_RX_CHAN(chan) (0 + NIFACES*(chan))
 #define MCDMA_FEC_TX_CHAN(chan) (1 + NIFACES*(chan))
 
@@ -150,19 +132,6 @@ extern char _SysSramBase[];
 				      +(chan)*(MCF548X_FEC1_TX_INITIATOR	\
 					       -MCF548X_FEC0_TX_INITIATOR))
 
-/*
- * RTEMS event used by interrupt handler to signal daemons.
- * This must *not* be the same event used by the TCP/IP task synchronization.
- */
-#define INTERRUPT_EVENT RTEMS_EVENT_1
-#define FATAL_INT_EVENT RTEMS_EVENT_3
-
-/*
- * RTEMS event used to start transmit daemon.
- * This must not be the same as INTERRUPT_EVENT.
- */
-#define START_TRANSMIT_EVENT RTEMS_EVENT_2
-
 /* BD and parameters are stored in SRAM(refer to sdma.h) */
 #define MCF548X_FEC_BD_BASE    ETH_BD_BASE
 
@@ -198,6 +167,12 @@ extern char _SysSramBase[];
 (MCF548X_FEC_EIMR_LC   | MCF548X_FEC_EIMR_RL    | \
  MCF548X_FEC_EIMR_XFUN | MCF548X_FEC_EIMR_XFERR | MCF548X_FEC_EIMR_RFERR)
 
+typedef enum {
+  FEC_STATE_RESTART_0,
+  FEC_STATE_RESTART_1,
+  FEC_STATE_NORMAL,
+} fec_state;
+
 /*
  * Device data
  */
@@ -206,12 +181,10 @@ struct mcf548x_enet_struct {
   struct mbuf             **rxMbuf;
   struct mbuf             **txMbuf;
   int                     chan;
+  fec_state               state;
   int                     acceptBroadcast;
   int                     rxBdCount;
   int                     txBdCount;
-  int                     txBdHead;
-  int                     txBdTail;
-  int                     txBdActiveCount;
   MCD_bufDescFec          *rxBd;
   MCD_bufDescFec          *txBd;
   int                     rxDmaChan; /* dma task */
@@ -232,13 +205,14 @@ struct mcf548x_enet_struct {
   unsigned long           rxGiant;
   unsigned long           rxNonOctet;
   unsigned long           rxBadCRC;
-  unsigned long           rxOverrun;
+  unsigned long           rxFIFOError;
   unsigned long           rxCollision;
 
   unsigned long           txInterrupts;
   unsigned long           txDeferred;
   unsigned long           txLateCollision;
   unsigned long           txUnderrun;
+  unsigned long           txFIFOError;
   unsigned long           txMisaligned;
   unsigned long           rxNotFirst;
   unsigned long           txRetryLimit;
@@ -246,70 +220,29 @@ struct mcf548x_enet_struct {
 
 static struct mcf548x_enet_struct enet_driver[NIFACES];
 
-extern int taskTable;
-static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc);
-
-
-
-/*
- * Function:	mcf548x_fec_rx_bd_init
- *
- * Description:	Initialize the receive buffer descriptor ring.
- *
- * Returns:		void
- *
- * Notes:       Space for the buffers of rx BDs is allocated by the rx deamon
- *
- */
-static void mcf548x_fec_rx_bd_init(struct mcf548x_enet_struct *sc) {
-  int rxBdIndex;
-  struct mbuf *m;
-  struct ifnet *ifp = &sc->arpcom.ac_if;
+static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc, rtems_id otherDaemon);
 
-  /*
-   * Fill RX buffer descriptor ring.
-   */
-  for( rxBdIndex = 0; rxBdIndex < sc->rxBdCount; rxBdIndex++ ) {
-    MGETHDR (m, M_WAIT, MT_DATA);
-    MCLGET (m, M_WAIT);
-
-    m->m_pkthdr.rcvif = ifp;
-    sc->rxMbuf[rxBdIndex] = m;
-    rtems_cache_invalidate_multiple_data_lines(mtod(m,const void *),
-					       ETHER_MAX_LEN);
-    SET_BD_BUFFER(sc->rxBd+rxBdIndex,mtod(m, void *));
-    SET_BD_LENGTH(sc->rxBd+rxBdIndex,ETHER_MAX_LEN);
-    SET_BD_STATUS(sc->rxBd+rxBdIndex,
-		  MCF548X_FEC_RBD_EMPTY
-		  | MCF548X_FEC_RBD_INT
-		  | ((rxBdIndex == sc->rxBdCount-1)
-		     ? MCF548X_FEC_RBD_WRAP
-		     : 0));
-  }
+static void fec_send_event(rtems_id task)
+{
+  rtems_bsdnet_event_send(task, FEC_EVENT);
 }
 
-/*
- * Function:	mcf548x_fec_rx_bd_cleanup
- *
- * Description:	put all mbufs pending in rx BDs back to buffer pool
- *
- * Returns:		void
- *
- */
-static void mcf548x_fec_rx_bd_cleanup(struct mcf548x_enet_struct *sc) {
-  int rxBdIndex;
-  struct mbuf *m,*n;
+static void fec_wait_for_event(void)
+{
+  rtems_event_set out;
+  rtems_bsdnet_event_receive(
+    FEC_EVENT,
+    RTEMS_EVENT_ANY | RTEMS_WAIT,
+    RTEMS_NO_TIMEOUT,
+    &out
+  );
+}
 
-  /*
-   * Drain RX buffer descriptor ring.
-   */
-  for( rxBdIndex = 0; rxBdIndex < sc->rxBdCount; rxBdIndex++ ) {
-    n = sc->rxMbuf[rxBdIndex];
-    while (n != NULL) {
-      m = n;
-      MFREE(m,n);
-    }
-  }
+static void mcf548x_fec_request_restart(struct mcf548x_enet_struct *sc)
+{
+  sc->state = FEC_STATE_RESTART_0;
+  fec_send_event(sc->txDaemonTid);
+  fec_send_event(sc->rxDaemonTid);
 }
 
 /*
@@ -349,7 +282,7 @@ static void mcf548x_eth_addr_filter_set(struct mcf548x_enet_struct *sc)  {
   * This is because the CRC generatore in hardware is implemented
   * as a shift-register with as many ex-ores as the radixes
   * in the polynomium. This suggests that we represent the
-  * polynomiumm itself as a 32-bit constant.
+  * polynomiumm itsc as a 32-bit constant.
   */
   for(byte = 0; byte < 6; byte++)
     {
@@ -539,7 +472,7 @@ static int mcf548x_eth_mii_write(
  * Notes:
  *
  */
-static int mcf548x_fec_reset(struct mcf548x_enet_struct *sc) {
+static void mcf548x_fec_reset(struct mcf548x_enet_struct *sc) {
   volatile int delay;
   int chan     = sc->chan;
   /*
@@ -566,8 +499,6 @@ static int mcf548x_fec_reset(struct mcf548x_enet_struct *sc) {
    * wait at least 16 clock cycles
    */
   for (delay = 0;delay < 16*4;delay++) {};
-
-  return true;
 }
 
 
@@ -647,13 +578,7 @@ void mcf548x_fec_off(struct mcf548x_enet_struct *sc)
   * Disable the Ethernet Controller
   */
   MCF548X_FEC_ECR(chan) &= ~(MCF548X_FEC_ECR_ETHER_EN);
-
-  /*
-   * cleanup all buffers
-   */
-  mcf548x_fec_rx_bd_cleanup(sc);
-
-  }
+}
 
 /*
  * MCF548X FEC interrupt handler
@@ -682,17 +607,18 @@ void mcf548x_fec_irq_handler(rtems_vector_number vector)
     sc->txUnderrun++;
   }
   if (ievent & MCF548X_FEC_EIR_XFERR) {
-    sc->txUnderrun++;
+    sc->txFIFOError++;
   }
   if (ievent & MCF548X_FEC_EIR_RFERR) {
-    sc->rxOverrun++;
+    sc->rxFIFOError++;
   }
   /*
    * fatal error ocurred?
    */
   if (ievent & (MCF548X_FEC_EIR_RFERR | MCF548X_FEC_EIR_XFERR)) {
     MCF548X_FEC_EIMR(chan) &=~(MCF548X_FEC_EIMR_RFERR | MCF548X_FEC_EIMR_XFERR);
-    rtems_bsdnet_event_send(sc->rxDaemonTid, FATAL_INT_EVENT);
+    printk("fifo\n");
+    mcf548x_fec_request_restart(sc);
   }
 }
 
@@ -708,7 +634,7 @@ void mcf548x_mcdma_rx_irq_handler(void * param)
 
     mcdma_glue_irq_disable(sc->rxDmaChan);/*Disable receive ints*/
     sc->rxInterrupts++; 		/* Rx int has occurred */
-    rtems_bsdnet_event_send(sc->rxDaemonTid, INTERRUPT_EVENT);
+    fec_send_event(sc->rxDaemonTid);
   }
 }
 
@@ -728,342 +654,10 @@ void mcf548x_mcdma_tx_irq_handler(void * param)
 
     sc->txInterrupts++; /* Tx int has occurred */
 
-    rtems_bsdnet_event_send(sc->txDaemonTid, INTERRUPT_EVENT);
-  }
-}
-
-
-
-
-
- /*
-  * Function:	    mcf548x_fec_retire_tbd
-  *
-  * Description:	Soak up buffer descriptors that have been sent.
-  *
-  * Returns:		void
-  *
-  * Notes:
-  *
-  */
-static void mcf548x_fec_retire_tbd(struct mcf548x_enet_struct *sc,
-				   bool force)
-{
-  struct mbuf *n;
-  /*
-   * Clear already transmitted BDs first. Will not work calling same
-   * from fecExceptionHandler(TFINT).
-   */
-
-  while ((sc->txBdActiveCount > 0) &&
-	 (force ||
-	  ((MCF548X_FEC_TBD_READY & GET_BD_STATUS(sc->txBd+sc->txBdTail))
-	   == 0x0))) {
-    if (sc->txMbuf[sc->txBdTail] != NULL) {
-      /*
-       * NOTE: txMbuf can be NULL, if mbuf has been split into different BDs
-       */
-      MFREE (sc->txMbuf[sc->txBdTail],n);
-      sc->txMbuf[sc->txBdTail] = NULL;
-    }
-    sc->txBdActiveCount--;
-    if(++sc->txBdTail >= sc->txBdCount) {
-      sc->txBdTail = 0;
-    }
-  }
-}
-
-
-static void mcf548x_fec_sendpacket(struct ifnet *ifp,struct mbuf *m) {
-  struct mcf548x_enet_struct *sc = ifp->if_softc;
-  struct mbuf *l = NULL;
-  int nAdded;
-  uint32_t status;
-  rtems_event_set events;
-  MCD_bufDescFec *thisBd;
-  MCD_bufDescFec *firstBd = NULL;
-  void *data_ptr;
-  size_t data_len;
-
- /*
-  * Free up buffer descriptors
-  */
-  mcf548x_fec_retire_tbd(sc,false);
-
- /*
-  * Set up the transmit buffer descriptors.
-  * No need to pad out short packets since the
-  * hardware takes care of that automatically.
-  * No need to copy the packet to a contiguous buffer
-  * since the hardware is capable of scatter/gather DMA.
-  */
-  nAdded = 0;
-
-  for(;;) {
-
-   /*
-    * Wait for buffer descriptor to become available.
-    */
-    if((sc->txBdActiveCount + nAdded) == sc->txBdCount) {
-
-      /*
-       * Clear old events
-       */
-      MCDMA_CLR_PENDING(sc->txDmaChan);
-      /*
-       * Wait for buffer descriptor to become available.
-       * Note that the buffer descriptors are checked
-       * *before* * entering the wait loop -- this catches
-       * the possibility that a buffer descriptor became
-       * available between the `if' above, and the clearing
-       * of the event register.
-       * This is to catch the case where the transmitter
-       * stops in the middle of a frame -- and only the
-       * last buffer descriptor in a frame can generate
-       * an interrupt.
-       */
-      mcf548x_fec_retire_tbd(sc,false);
-
-      while((sc->txBdActiveCount + nAdded) == sc->txBdCount) {
-	mcdma_glue_irq_enable(sc->txDmaChan);
-	rtems_bsdnet_event_receive(INTERRUPT_EVENT,
-				   RTEMS_WAIT | RTEMS_EVENT_ANY,
-				   RTEMS_NO_TIMEOUT, &events);
-        mcf548x_fec_retire_tbd(sc,false);
-      }
-    }
-
-    if(m->m_len == 0) {
-      /*
-       * Just toss empty mbufs
-       */
-      struct mbuf *n;
-      MFREE(m, n);
-      m = n;
-      if(l != NULL) {
-        l->m_next = m;
-      }
-    }
-    else {
-      /*
-       * Flush the buffer for this descriptor
-       */
-      rtems_cache_flush_multiple_data_lines((const void *)mtod(m, void *),
-					    m->m_len);
-      /*
-       * Fill in the buffer descriptor,
-       * set "end of frame" bit in status,
-       * if last mbuf in chain
-       */
-      thisBd = sc->txBd + sc->txBdHead;
-      /*
-       * FIXME: do not send interrupt after every frame
-       * doing this every quarter of BDs is much more efficent
-       */
-      status = (((m->m_next == NULL)
-		 ? MCF548X_FEC_TBD_LAST | MCF548X_FEC_TBD_INT
-		 : 0)
-		| ((sc->txBdHead == sc->txBdCount-1)
-		   ? MCF548X_FEC_TBD_WRAP
-		   :0 ));
-      /*
-       * Don't set the READY flag till the
-       * whole packet has been readied.
-       */
-      if (firstBd != NULL) {
-	status |= MCF548X_FEC_TBD_READY;
-      }
-      else {
-	firstBd = thisBd;
-      }
-
-      data_ptr = mtod(m, void *);
-      data_len = m->m_len;
-      sc->txMbuf[sc->txBdHead] = m;
-      /* go to next part in chain */
-      l = m;
-      m = m->m_next;
-
-      SET_BD_BUFFER(thisBd, data_ptr);
-      SET_BD_LENGTH(thisBd, data_len);
-      SET_BD_STATUS(thisBd, status);
-
-      nAdded++;
-      if(++(sc->txBdHead) == sc->txBdCount) {
-        sc->txBdHead = 0;
-      }
-    }
-    /*
-     * Set the transmit buffer status.
-     * Break out of the loop if this mbuf is the last in the frame.
-     */
-    if(m == NULL) {
-      if(nAdded) {
-	SET_BD_STATUS(firstBd,
-		      GET_BD_STATUS(firstBd) | MCF548X_FEC_TBD_READY);
-	MCD_continDma(sc->txDmaChan);
-        sc->txBdActiveCount += nAdded;
-      }
-      break;
-    }
-  } /* end of for(;;) */
-}
-
-
-/*
- * Driver transmit daemon
- */
-void mcf548x_fec_txDaemon(void *arg)
-  {
-  struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)arg;
-  struct ifnet *ifp = &sc->arpcom.ac_if;
-  struct mbuf *m;
-  rtems_event_set events;
-
-  for(;;) {
-   /*
-    * Wait for packet
-    */
-    mcdma_glue_irq_enable(sc->txDmaChan);
-    rtems_bsdnet_event_receive(START_TRANSMIT_EVENT|INTERRUPT_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;
-
-      mcf548x_fec_sendpacket(ifp, m);
-
-      }
-
-    ifp->if_flags &= ~IFF_OACTIVE;
-
-    }
-
-  }
-
-
-/*
- * reader task
- */
-static void mcf548x_fec_rxDaemon(void *arg){
-  struct mcf548x_enet_struct *sc = (struct mcf548x_enet_struct *)arg;
-  struct ifnet *ifp = &sc->arpcom.ac_if;
-  struct mbuf *m;
-  struct ether_header *eh;
-  int rxBdIndex;
-  uint32_t status;
-  size_t size;
-  rtems_event_set events;
-  size_t len = 1;
-  MCD_bufDescFec *bd;
-
-  /*
-   * Input packet handling loop
-   */
-  rxBdIndex = 0;
-
-  for (;;) {
-    /*
-     * Clear old events
-     */
-    MCDMA_CLR_PENDING(sc->rxDmaChan);
-    /*
-     * Get the first BD pointer and its length.
-     */
-    bd     = sc->rxBd + rxBdIndex;
-    status = GET_BD_STATUS( bd );
-    len    = GET_BD_LENGTH( bd );
-
-    /*
-     * Loop through BDs until we find an empty one. This indicates that
-     * the DMA is still using it.
-     */
-    while( !(status & MCF548X_FEC_RBD_EMPTY) ) {
-
-      /*
-       * Remember the data pointer from this transfer.
-       */
-      GET_BD_BUFFER(bd);
-      m    = sc->rxMbuf[rxBdIndex];
-      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);
-      ether_input(ifp, eh, m);
-
-      /*
-       * Done w/ the BD. Clean it.
-       */
-      sc->rxMbuf[rxBdIndex] = NULL;
-
-      /*
-       * Add a new buffer to the ring.
-       */
-      MGETHDR (m, M_WAIT, MT_DATA);
-      MCLGET (m, M_WAIT);
-      m->m_pkthdr.rcvif = ifp;
-      size = ETHER_MAX_LEN;
-
-      sc->rxMbuf[rxBdIndex] = m;
-      rtems_cache_invalidate_multiple_data_lines(mtod(m,const void *),
-						 size);
-
-      SET_BD_BUFFER(bd,mtod(m, void *));
-      SET_BD_LENGTH(bd,size);
-      SET_BD_STATUS(bd,
-		    MCF548X_FEC_RBD_EMPTY
-		    |MCF548X_FEC_RBD_INT
-		    |((rxBdIndex == sc->rxBdCount-1)
-		      ? MCF548X_FEC_RBD_WRAP
-		      : 0)
-		    );
-
-      /*
-       * advance to next BD
-       */
-      if (++rxBdIndex >= sc->rxBdCount) {
-	rxBdIndex = 0;
-      }
-      /*
-       * Get next BD pointer and its length.
-       */
-      bd     = sc->rxBd + rxBdIndex;
-      status = GET_BD_STATUS( bd );
-      len    = GET_BD_LENGTH( bd );
-    }
-    /*
-     * Unmask RXF (Full frame received) event
-     */
-    mcdma_glue_irq_enable(sc->rxDmaChan);
-
-    rtems_bsdnet_event_receive (INTERRUPT_EVENT | FATAL_INT_EVENT,
-				RTEMS_WAIT | RTEMS_EVENT_ANY,
-				RTEMS_NO_TIMEOUT, &events);
-    if (events & FATAL_INT_EVENT) {
-      /*
-       * fatal interrupt ocurred, so reinit fec and restart mcdma tasks
-       */
-      mcf548x_fec_restart(sc);
-      rxBdIndex = 0;
-    }
+    fec_send_event(sc->txDaemonTid);
   }
 }
 
-
 /*
  * Function:	mcf548x_fec_initialize_hardware
  *
@@ -1136,8 +730,8 @@ static void mcf548x_fec_initialize_hardware(struct mcf548x_enet_struct *sc)
   /*
    * Set transmit fifo watermark register (X_WMRK), default = 64
    */
-  MCF548X_FEC_FECTFAR(chan) = MCF548X_FEC_FECTFAR_ALARM(256);	/* 256 bytes */
-  MCF548X_FEC_FECTFWR(chan) = MCF548X_FEC_FECTFWR_X_WMRK_64;	/* 64 bytes */
+  MCF548X_FEC_FECTFAR(chan) = MCF548X_FEC_FECTFAR_ALARM(128);
+  MCF548X_FEC_FECTFWR(chan) = MCF548X_FEC_FECTFWR_X_WMRK_64;   /* 64 bytes */
 
  /*
   * Set individual address filter for unicast address
@@ -1168,15 +762,11 @@ static void mcf548x_fec_tx_start(struct ifnet *ifp)
 
   ifp->if_flags |= IFF_OACTIVE;
 
-  rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
+  fec_send_event(sc->txDaemonTid);
 
   }
 
-
-/*
- * start the DMA channel
- */
-static void mcf548x_fec_startDMA(struct mcf548x_enet_struct *sc)
+static void fec_start_dma_and_controller(struct mcf548x_enet_struct *sc)
 {
   int chan = sc->chan;
   int mcdma_rc;
@@ -1231,7 +821,384 @@ static void mcf548x_fec_startDMA(struct mcf548x_enet_struct *sc)
       if (mcdma_rc != MCD_OK) {
 	rtems_panic("FEC: cannot start tx DMA");
       }
+
+  /*
+   * Enable FEC-Lite controller
+   */
+  MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_ETHER_EN;
+}
+
+static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc, rtems_id otherDaemon)
+{
+  if (sc->state == FEC_STATE_RESTART_1) {
+    mcf548x_fec_initialize_hardware(sc);
+    fec_start_dma_and_controller(sc);
+    sc->state = FEC_STATE_NORMAL;
+  } else {
+    sc->state = FEC_STATE_RESTART_1;
+  }
+
+  fec_send_event(otherDaemon);
+  while (sc->state != FEC_STATE_NORMAL) {
+    fec_wait_for_event();
+  }
+}
+
+static void fec_reset_bd_and_discard_tx_frames(
+  int bdCount,
+  MCD_bufDescFec *bdRing,
+  struct mbuf **mbufs
+)
+{
+  int bdIndex = 0;
+
+  for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+    bool bdIsLast = bdIndex == bdCount - 1;
+    struct mbuf *m = mbufs[bdIndex];
+
+    bdRing[bdIndex].statCtrl = bdIsLast ? MCF548X_FEC_TBD_WRAP : 0;
+
+    if (m != NULL) {
+      mbufs[bdIndex] = NULL;
+      m_free(m);
+    }
+  }
+}
+
+static void fec_reset_tx_dma(
+  int dmaChan,
+  int bdCount,
+  MCD_bufDescFec *bdRing,
+  struct mbuf **mbufs,
+  struct mbuf *m
+)
+{
+  if (m != NULL) {
+    m_freem(m);
+  }
+
+  MCD_killDma(dmaChan);
+
+  fec_reset_bd_and_discard_tx_frames(bdCount, bdRing, mbufs);
+}
+
+static struct mbuf *fec_next_fragment(
+  struct ifnet *ifp,
+  struct mbuf *m,
+  bool *isFirst
+)
+{
+  struct mbuf *n = NULL;
+
+  *isFirst = false;
+
+  while (true) {
+    if (m == NULL) {
+      IF_DEQUEUE(&ifp->if_snd, m);
+
+      if (m != NULL) {
+        *isFirst = true;
+      } else {
+        ifp->if_flags &= ~IFF_OACTIVE;
+
+        return NULL;
+      }
+    }
+
+    if (m->m_len > 0) {
+      break;
+    } else {
+      m = m_free(m);
+    }
+  }
+
+  n = m->m_next;
+  while (n != NULL && n->m_len <= 0) {
+    n = m_free(n);
+  }
+  m->m_next = n;
+
+  return m;
+}
+
+static bool fec_transmit(
+  struct ifnet *ifp,
+  int dmaChan,
+  int bdCount,
+  MCD_bufDescFec *bdRing,
+  struct mbuf **mbufs,
+  int *bdIndexPtr,
+  struct mbuf **mPtr,
+  MCD_bufDescFec **firstPtr
+)
+{
+  bool bdShortage = false;
+  int bdIndex = *bdIndexPtr;
+  struct mbuf *m = *mPtr;
+  MCD_bufDescFec *first = *firstPtr;
+
+  while (true) {
+    MCD_bufDescFec *bd = &bdRing[bdIndex];
+
+    MCDMA_CLR_PENDING(dmaChan);
+    if ((bd->statCtrl & MCF548X_FEC_TBD_READY) == 0) {
+      struct mbuf *done = mbufs[bdIndex];
+      bool isFirst = false;
+
+      if (done != NULL) {
+        m_free(done);
+        mbufs[bdIndex] = NULL;
+      }
+
+      m = fec_next_fragment(ifp, m, &isFirst);
+      if (m != NULL) {
+        bool bdIsLast = bdIndex == bdCount - 1;
+        u16 status = bdIsLast ? MCF548X_FEC_TBD_WRAP : 0;
+
+        bd->length = (u16) m->m_len;
+        bd->dataPointer = mtod(m, u32);
+
+        mbufs[bdIndex] = m;
+
+        rtems_cache_flush_multiple_data_lines(mtod(m, void *), m->m_len);
+
+        if (isFirst) {
+          first = bd;
+        } else {
+          status |= MCF548X_FEC_TBD_READY;
+        }
+
+        if (m->m_next != NULL) {
+          bd->statCtrl = status;
+        } else {
+          bd->statCtrl = status | MCF548X_FEC_TBD_INT | MCF548X_FEC_TBD_LAST;
+          first->statCtrl |= MCF548X_FEC_TBD_READY;
+          MCD_continDma(dmaChan);
+        }
+
+        m = m->m_next;
+      } else {
+        break;
+      }
+    } else {
+      bdShortage = true;
+      break;
+    }
+
+    if (bdIndex < bdCount - 1) {
+      ++bdIndex;
+    } else {
+      bdIndex = 0;
+    }
+  }
+
+  *bdIndexPtr = bdIndex;
+  *mPtr = m;
+  *firstPtr = first;
+
+  return bdShortage;
+}
+
+static MCD_bufDescFec *fec_init_tx_dma(
+  MCD_bufDescFec *bdRing,
+  int bdCount
+)
+{
+  int bdIndex;
+
+  for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+    bool bdIsLast = bdIndex == bdCount - 1;
+
+    bdRing[bdIndex].dataPointer = 0;
+    bdRing[bdIndex].length = 0;
+    bdRing[bdIndex].statCtrl = bdIsLast ? MCF548X_FEC_RBD_WRAP : 0;
+  }
+
+  return bdRing;
+}
+
+static void mcf548x_fec_txDaemon(void *arg)
+{
+  struct mcf548x_enet_struct *sc = arg;
+  struct ifnet *ifp = &sc->arpcom.ac_if;
+  int dmaChan = sc->txDmaChan;
+  int bdIndex = 0;
+  int bdCount = sc->txBdCount;
+  struct mbuf **mbufs = &sc->txMbuf[0];
+  struct mbuf *m = NULL;
+  MCD_bufDescFec *bdRing = fec_init_tx_dma(sc->txBd, bdCount);
+  MCD_bufDescFec *first = NULL;
+  bool bdShortage = false;
+
+  memset(mbufs, 0, bdCount * sizeof(*mbufs));
+
+  while (true) {
+    if (bdShortage) {
+      mcdma_glue_irq_enable(dmaChan);
+    }
+    fec_wait_for_event();
+
+    if (sc->state != FEC_STATE_NORMAL) {
+      fec_reset_tx_dma(dmaChan, bdCount, bdRing, mbufs, m);
+      mcf548x_fec_restart(sc, sc->rxDaemonTid);
+      bdIndex = 0;
+      m = NULL;
+      first = NULL;
+    }
+
+    bdShortage = fec_transmit(
+      ifp,
+      dmaChan,
+      bdCount,
+      bdRing,
+      mbufs,
+      &bdIndex,
+      &m,
+      &first
+    );
+  }
+}
+
+static struct mbuf *fec_add_mbuf(
+  int how,
+  struct ifnet *ifp,
+  MCD_bufDescFec *bd,
+  bool bdIsLast
+)
+{
+  struct mbuf *m;
+
+  MGETHDR(m, how, MT_DATA);
+  if (m != NULL) {
+    MCLGET(m, how);
+    if ((m->m_flags & M_EXT) != 0) {
+      m->m_pkthdr.rcvif = ifp;
+
+      rtems_cache_invalidate_multiple_data_lines(mtod(m, void *), ETHER_MAX_LEN);
+
+      bd->dataPointer = mtod(m, u32);
+      bd->length = ETHER_MAX_LEN;
+      bd->statCtrl = MCF548X_FEC_RBD_EMPTY
+        | MCF548X_FEC_RBD_INT
+        | (bdIsLast ? MCF548X_FEC_RBD_WRAP : 0);
+    } else {
+      m_free(m);
+    }
+  }
+
+  return m;
+}
+
+static MCD_bufDescFec *fec_init_rx_dma(
+  MCD_bufDescFec *bdRing,
+  struct ifnet *ifp,
+  int bdCount,
+  struct mbuf **mbufs
+)
+{
+  int bdIndex;
+
+  for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+    bool bdIsLast = bdIndex == bdCount - 1;
+
+    mbufs[bdIndex] = fec_add_mbuf(M_WAIT, ifp, &bdRing[bdIndex], bdIsLast);
+  }
+
+  return bdRing;
+}
+
+static void fec_reset_rx_dma(
+  int dmaChan,
+  int bdCount,
+  MCD_bufDescFec *bdRing
+)
+{
+  int bdIndex;
+
+  MCD_killDma(dmaChan);
+
+  for (bdIndex = 0; bdIndex < bdCount - 1; ++bdIndex) {
+    bdRing[bdIndex].length = ETHER_MAX_LEN;
+    bdRing[bdIndex].statCtrl = MCF548X_FEC_RBD_EMPTY | MCF548X_FEC_RBD_INT;
+  }
+
+  bdRing[bdIndex].length = ETHER_MAX_LEN;
+  bdRing[bdIndex].statCtrl = MCF548X_FEC_RBD_EMPTY | MCF548X_FEC_RBD_INT | MCF548X_FEC_RBD_WRAP;
+}
+
+static int fec_ether_input(
+  struct ifnet *ifp,
+  int dmaChan,
+  int bdIndex,
+  int bdCount,
+  MCD_bufDescFec *bdRing,
+  struct mbuf **mbufs
+)
+{
+  while (true) {
+    bool bdIsLast = bdIndex == bdCount - 1;
+    MCD_bufDescFec *bd = &bdRing[bdIndex];
+    struct mbuf *m = mbufs[bdIndex];
+    struct mbuf *n;
+    u16 status;
+
+    MCDMA_CLR_PENDING(dmaChan);
+    status = bd->statCtrl;
+
+    if ((status & MCF548X_FEC_RBD_EMPTY) != 0) {
+      break;
+    }
+
+    n = fec_add_mbuf(0, ifp, bd, bdIsLast);
+    if (n != NULL) {
+      int len = bd->length - ETHER_HDR_LEN - ETHER_CRC_LEN;
+      struct ether_header *eh = mtod(m, struct ether_header *);
+
+      m->m_len = len;
+      m->m_pkthdr.len = len;
+      m->m_data = mtod(m, char *) + ETHER_HDR_LEN;
+
+      ether_input(ifp, eh, m);
+    } else {
+      n = m;
+    }
+
+    mbufs[bdIndex] = n;
+
+    if (bdIndex < bdCount - 1) {
+      ++bdIndex;
+    } else {
+      bdIndex = 0;
+    }
+  }
+
+  return bdIndex;
+}
+
+static void mcf548x_fec_rxDaemon(void *arg)
+{
+  struct mcf548x_enet_struct *sc = arg;
+  struct ifnet *ifp = &sc->arpcom.ac_if;
+  int dmaChan = sc->rxDmaChan;
+  int bdIndex = 0;
+  int bdCount = sc->rxBdCount;
+  struct mbuf **mbufs = &sc->rxMbuf[0];
+  MCD_bufDescFec *bdRing = fec_init_rx_dma(sc->rxBd, ifp, bdCount, mbufs);
+
+  while (true) {
+    mcdma_glue_irq_enable(dmaChan);
+    fec_wait_for_event();
+
+    bdIndex = fec_ether_input(ifp, dmaChan, bdIndex, bdCount, bdRing, mbufs);
+
+    if (sc->state != FEC_STATE_NORMAL) {
+      fec_reset_rx_dma(dmaChan, bdCount, bdRing);
+      mcf548x_fec_restart(sc, sc->txDaemonTid);
+      bdIndex = 0;
+    }
+  }
 }
+
 /*
  * Initialize and start the device
  */
@@ -1292,12 +1259,6 @@ static void mcf548x_fec_init(void *arg)
       bsp_interrupt_vector_enable(MCF548X_IRQ_FEC(chan));
 
       MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED;
-      mcf548x_fec_rx_bd_init(sc);
-
-      /*
-       * reset and Set up mcf548x FEC hardware
-       */
-      mcf548x_fec_initialize_hardware(sc);
 
       /*
        * Start driver tasks
@@ -1308,24 +1269,10 @@ static void mcf548x_fec_init(void *arg)
 					     mcf548x_fec_txDaemon, sc);
       sc->rxDaemonTid = rtems_bsdnet_newproc(rxTaskName, 4096,
 					     mcf548x_fec_rxDaemon, sc);
-      /*
-       * Clear SmartDMA task interrupt pending bits.
-       */
-      MCDMA_CLR_PENDING(sc->rxDmaChan );
-      MCDMA_CLR_PENDING(sc->txDmaChan );
-
-      /*
-       * start the DMA channels
-       */
-      mcf548x_fec_startDMA(sc);
-      /*
-       * Enable FEC-Lite controller
-       */
-      MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_ETHER_EN;
-
-
     }
 
+  mcf548x_fec_request_restart(sc);
+
   /*
    * Set flags appropriately
    */
@@ -1347,109 +1294,25 @@ static void mcf548x_fec_init(void *arg)
 
 static void enet_stats (struct mcf548x_enet_struct *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 ("       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 ("  Late Collision:%-8lu\n", sc->txLateCollision);
-  printf ("   Retransmit Limit:%-8lu", sc->txRetryLimit);
-  printf ("        Underrun:%-8lu", sc->txUnderrun);
-  printf ("      Misaligned:%-8lu\n", sc->txMisaligned);
+  printf ("       Rx Interrupts:%-8lu", sc->rxInterrupts);
+  printf ("        Rx Not First:%-8lu", sc->rxNotFirst);
+  printf ("         Rx Not Last:%-8lu\n", sc->rxNotLast);
+  printf ("            Rx Giant:%-8lu", sc->rxGiant);
+  printf ("        Rx Non-octet:%-8lu", sc->rxNonOctet);
+  printf ("          Rx Bad CRC:%-8lu\n", sc->rxBadCRC);
+  printf ("       Rx FIFO Error:%-8lu", sc->rxFIFOError);
+  printf ("        Rx Collision:%-8lu", sc->rxCollision);
+
+  printf ("       Tx Interrupts:%-8lu\n", sc->txInterrupts);
+  printf ("         Tx Deferred:%-8lu", sc->txDeferred);
+  printf ("   Tx Late Collision:%-8lu", sc->txLateCollision);
+  printf (" Tx Retransmit Limit:%-8lu\n", sc->txRetryLimit);
+  printf ("         Tx Underrun:%-8lu", sc->txUnderrun);
+  printf ("       Tx FIFO Error:%-8lu", sc->txFIFOError);
+  printf ("       Tx Misaligned:%-8lu\n", sc->txMisaligned);
 
 }
 
-/*
- * restart the driver, reinit the fec
- * this function is responsible to reinitialize the FEC in case a fatal
- * error has ocurred. This is needed, wen a RxFIFO Overrun or a TxFIFO underrun
- * has ocurred. In these cases, the FEC is automatically disabled, and
- * both FIFOs must be reset and the BestComm tasks must be restarted
- *
- * Note: the daemon tasks will continue to run
- * (in fact this function will be called in the context of the rx daemon task)
- */
-#define NEW_DMA_SETUP
-
-static void mcf548x_fec_restart(struct mcf548x_enet_struct *sc)
-{
-  int chan = sc->chan;
-  /*
-   * FIXME: bring Tx Daemon into idle state
-   */
-#ifdef NEW_DMA_SETUP
-  /*
-   * cleanup remaining receive mbufs
-   */
-  mcf548x_fec_rx_bd_cleanup(sc);
-#endif
-  /*
-   * Stop DMA tasks
-   */
-  MCD_killDma (sc->rxDmaChan);
-  MCD_killDma (sc->txDmaChan);
-  /*
-   * FIXME: wait, until Tx Daemon is in idle state
-   */
-
-  /*
-   * Disable transmit / receive interrupts
-   */
-  mcdma_glue_irq_disable(sc->txDmaChan);
-  mcdma_glue_irq_disable(sc->rxDmaChan);
-#ifdef NEW_DMA_SETUP
-  /*
-   * recycle pending tx buffers
-   * FIXME: try to extract pending Tx buffers
-   */
-  mcf548x_fec_retire_tbd(sc,true);
-#endif
-  /*
-   * re-initialize the FEC hardware
-   */
-  mcf548x_fec_initialize_hardware(sc);
-
-#ifdef NEW_DMA_SETUP
-
-  /*
-   * reinit receive mbufs
-   */
-  mcf548x_fec_rx_bd_init(sc);
-#endif
-  /*
-   * Clear SmartDMA task interrupt pending bits.
-   */
-  MCDMA_CLR_PENDING( sc->rxDmaChan );
-
-  /*
-   * start the DMA channels again
-   */
-  mcf548x_fec_startDMA(sc);
-  /*
-   * reenable rx/tx interrupts
-   */
-  mcdma_glue_irq_enable(sc->rxDmaChan);
-  mcdma_glue_irq_enable(sc->txDmaChan);
-  /*
-   * (re-)init fec hardware
-   */
-  mcf548x_fec_initialize_hardware(sc);
-  /*
-   * reenable fec FIFO error interrupts
-   */
-  MCF548X_FEC_EIMR(chan) = FEC_INTR_MASK_USED;
-  /*
-   * Enable FEC-Lite controller
-   */
-  MCF548X_FEC_ECR(chan) |= MCF548X_FEC_ECR_ETHER_EN;
-}
-
 int32_t mcf548x_fec_setMultiFilter(struct ifnet *ifp)
 {
   /*struct mcf548x_enet_struct *sc = ifp->if_softc; */




More information about the vc mailing list