This is the mail archive of the ecos-patches@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PPC QUICC ethernet patch


Dudes,

I'm intending to apply this patch to the quicc ethernet device
soon. Those interested, that's you Gary, should give it the once-over
first to check that it's to their liking.

Index: ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/eth/powerpc/quicc/current/ChangeLog,v
retrieving revision 1.17
diff -u -5 -r1.17 ChangeLog
--- ChangeLog	7 Mar 2003 03:47:47 -0000	1.17
+++ ChangeLog	14 Mar 2003 18:12:43 -0000
@@ -1,5 +1,12 @@
+2003-03-14  Nick Garnett  <nickg at balti dot calivar dot com>
+
+	* src/if_quicc.c: Fixed several bugs, mostly dealing with getting
+	the device restarted after certain failures such as collisions.
+
+	* src/quicc_eth.h: Added some statistics gathering.
+	
 2003-03-06  Gary Thomas  <gary at mlbassoc dot com>
 
 	* src/if_quicc.c (quicc_eth_init): New name for CPM/DPRAM allocator.
 
 2002-11-25  Gary Thomas  <gthomas at ecoscentric dot com>
Index: src/if_quicc.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/eth/powerpc/quicc/current/src/if_quicc.c,v
retrieving revision 1.16
diff -u -5 -r1.16 if_quicc.c
--- src/if_quicc.c	7 Mar 2003 03:47:47 -0000	1.16
+++ src/if_quicc.c	14 Mar 2003 18:12:44 -0000
@@ -368,11 +368,11 @@
 
     // Clear any pending interrupt/exceptions
     scc->scc_scce = 0xFFFF;
 
     // Enable interrupts
-    scc->scc_sccm = QUICC_SCCE_INTS;
+    scc->scc_sccm = QUICC_SCCE_INTS | QUICC_SCCE_GRC | QUICC_SCCE_BSY;
 
     // Set up SCCx to run in ethernet mode
     scc->scc_gsmr_h = 0;
     scc->scc_gsmr_l = QUICC_SCC_GSML_TCI | QUICC_SCC_GSML_TPL_48 |
         QUICC_SCC_GSML_TPP_01 | QUICC_SCC_GSML_MODE_ENET;
@@ -452,10 +452,69 @@
 {
     switch (key) {
     case ETH_DRV_SET_MAC_ADDRESS:
         return 0;
         break;
+
+#ifdef ETH_DRV_GET_IF_STATS
+    case ETH_DRV_GET_IF_STATS:
+    {
+        struct ether_drv_stats *p = (struct ether_drv_stats *)data;
+        struct quicc_eth_info *qi = (struct quicc_eth_info *)sc->driver_private;
+
+        strcpy( p->description, "QUICC (MPC8xx) SCC Ethernet" );
+        CYG_ASSERT( 48 > strlen(p->description), "Description too long" );
+
+        // Really need to determine the following values properly, for
+        // now just assume the link is up, full duplex, unknown speed.
+        
+        p->operational = 3;            // LINK UP
+        p->duplex = 1;
+        p->speed = 0;
+
+        {
+            p->supports_dot3        = false;
+
+            // Those commented out are not available on this chip.
+
+            p->tx_good              = qi->tx_good             ;
+            //p->tx_max_collisions    = qi->tx_max_collisions ;
+            p->tx_late_collisions   = qi->tx_late_collisions  ;
+            p->tx_underrun          = qi->tx_underrun         ;
+            p->tx_carrier_loss      = qi->tx_carrier_loss     ;
+            p->tx_deferred          = qi->tx_deferred         ;
+            //p->tx_sqetesterrors   = qi->tx_sqetesterrors    ;
+            //p->tx_single_collisions = qi->tx_single_collisions;
+            //p->tx_mult_collisions   = qi->tx_mult_collisions  ;
+            //p->tx_total_collisions  = qi->tx_total_collisions ;
+            p->rx_good              = qi->rx_good             ;
+            p->rx_crc_errors        = qi->rx_crc_errors       ;
+            p->rx_align_errors      = qi->rx_align_errors     ;
+            p->rx_resource_errors   = qi->rx_resource_errors  ;
+            p->rx_overrun_errors    = qi->rx_overrun_errors   ;
+            p->rx_collisions        = qi->rx_collisions       ;
+            p->rx_short_frames      = qi->rx_short_frames     ;
+            p->rx_too_long_frames   = qi->rx_long_frames      ;
+            //p->rx_symbol_errors   = qi->rx_symbol_errors    ;
+        
+            p->interrupts           = qi->interrupts          ;
+            p->rx_count             = qi->rx_count            ;
+            p->rx_deliver           = qi->rx_deliver          ;
+            p->rx_resource          = qi->rx_resource         ;
+            p->rx_restart           = qi->rx_restart          ;
+            p->tx_count             = qi->tx_count            ;
+            p->tx_complete          = qi->tx_complete         ;
+            p->tx_dropped           = qi->tx_dropped          ;
+        }
+
+        p->tx_queue_len = CYGNUM_DEVS_ETH_POWERPC_QUICC_TxNUM;
+
+        return 0; // OK
+    }
+#endif
+
+        
     default:
         return 1;
         break;
     }
 }
@@ -483,27 +542,21 @@
     volatile struct cp_bufdesc *txbd, *txfirst;
     volatile char *bp;
     int i, txindex, cache_state;
     unsigned int ctrl;
 
+    qi->tx_count++;
+    
     // Find a free buffer
     txbd = txfirst = qi->txbd;
-    while (txbd->ctrl & QUICC_BD_CTL_Ready) {
-        // This buffer is busy, move to next one
-        if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
-            txbd = qi->tbase;
-        } else {
-            txbd++;
-        }
-        if (txbd == txfirst) {
+    if ((txbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int )))
 #ifdef CYGPKG_NET
             panic ("No free xmit buffers");
 #else
             diag_printf("QUICC Ethernet: No free xmit buffers\n");
 #endif
-        }
-    }
+
     // Remember the next buffer to try
     if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
         qi->txbd = qi->tbase;
     } else {
         qi->txbd = txbd+1;
@@ -543,23 +596,73 @@
 static void
 quicc_eth_RxEvent(struct eth_drv_sc *sc)
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info *)sc->driver_private;
     volatile struct cp_bufdesc *rxbd;
+	
 
     rxbd = qi->rnext;
     while ((rxbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int)) == QUICC_BD_CTL_Int) {
-        qi->rxbd = rxbd;  // Save for callback
-        set_led(LED_RxACTIVE);
-        (sc->funs->eth_drv->recv)(sc, rxbd->length);
-        clear_led(LED_RxACTIVE);
-        rxbd->ctrl |= QUICC_BD_CTL_Ready;
-        if (rxbd->ctrl & QUICC_BD_CTL_Wrap) {
+
+	qi->rx_count++;
+        
+        if( rxbd->ctrl & QUICC_BD_RX_MISS )
+        {
+            qi->rx_miss++;
+        }
+        if( rxbd->ctrl & QUICC_BD_RX_LG )
+        {
+            qi->rx_long_frames++;
+        }
+        if( rxbd->ctrl & QUICC_BD_RX_NO )
+        {
+            qi->rx_align_errors++;
+        }
+        if( rxbd->ctrl & QUICC_BD_RX_SH )
+        {
+            qi->rx_short_frames++;
+        }
+        if( rxbd->ctrl & QUICC_BD_RX_CR )
+        {
+            qi->rx_crc_errors++;
+        }
+        if( rxbd->ctrl & QUICC_BD_RX_OV )
+        {
+            qi->rx_overrun_errors++;
+        }
+
+        if( rxbd->ctrl & QUICC_BD_RX_CL )
+        {
+            qi->rx_collisions++;
+        }
+
+        if( (rxbd->ctrl & QUICC_BD_RX_ERRORS) == 0 )
+        {
+            qi->rx_good++;
+			
+            // OK frame - Prepare for callback
+            qi->rxbd = rxbd;  // Save for callback
+            set_led(LED_RxACTIVE);
+			
+            (sc->funs->eth_drv->recv)(sc, rxbd->length);
+			
+            clear_led(LED_RxACTIVE);
+        }
+        
+      
+        // Clear flags and wrap if needed else step up BD pointer 
+        if (rxbd->ctrl & QUICC_BD_CTL_Wrap) 
+        {
+            rxbd->ctrl = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int | QUICC_BD_CTL_Wrap;
             rxbd = qi->rbase;
-        } else {
+        } 
+        else 
+        {
+            rxbd->ctrl = QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int;
             rxbd++;
         }
+        
     }
     // Remember where we left off
     qi->rnext = (struct cp_bufdesc *)rxbd;
 }
 
@@ -574,11 +677,12 @@
 quicc_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info *)sc->driver_private;
     unsigned char *bp;
     int i, cache_state;
-
+    int sg_list_null_buffer = 0;
+    
     bp = (unsigned char *)qi->rxbd->buffer;
     // Note: the MPC8xx does not seem to snoop/invalidate the data cache properly!
     HAL_DCACHE_IS_ENABLED(cache_state);
     if (cache_state) {
         HAL_DCACHE_INVALIDATE(qi->rxbd->buffer, qi->rxbd->length);  // Make sure no stale data
@@ -586,36 +690,95 @@
     for (i = 0;  i < sg_len;  i++) {
         if (sg_list[i].buf != 0) {
             memcpy((void *)sg_list[i].buf, bp, sg_list[i].len);
             bp += sg_list[i].len;
         }
+        else
+            sg_list_null_buffer = 1;
     }
+
+    // A NULL sg_list buffer usually means no mbufs, so we don't count
+    // it as a delivery, instead we count it as a resource error.
+    
+    if (!sg_list_null_buffer)
+        qi->rx_deliver++;
+    else
+        qi->rx_resource++;
+
+}
+
+
+static void
+quicc_eth_command( struct eth_drv_sc *sc, unsigned long cmd)
+{
+   volatile EPPC *eppc = (volatile EPPC *)eppc_base();
+   
+   eppc->cp_cr = QUICC_CPM_SCCx | cmd | QUICC_CPM_CR_BUSY;
+
+   while (eppc->cp_cr & QUICC_CPM_CR_BUSY )
+       continue;
 }
 
 static void
 quicc_eth_TxEvent(struct eth_drv_sc *sc, int stat)
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info *)sc->driver_private;
     volatile struct cp_bufdesc *txbd;
     int txindex;
+    bool restart = false;
 
     txbd = qi->tnext;
+    
     while ((txbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int)) == QUICC_BD_CTL_Int) {
+
         txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
-        txbd->ctrl &= ~QUICC_BD_CTL_Int;  // Reset int pending bit
+
+        qi->tx_complete++;
+        
         (sc->funs->eth_drv->tx_done)(sc, qi->txkey[txindex], 0);
+        txbd->ctrl &= ~QUICC_BD_CTL_Int;  // Reset int pending bit
+
+        if (txbd->ctrl & QUICC_BD_TX_LC )
+            qi->tx_late_collisions++, restart = true;
+        if (txbd->ctrl & QUICC_BD_TX_RL )
+            qi->tx_retransmit_error++, restart = true;
+        if (txbd->ctrl & QUICC_BD_TX_UN )
+            qi->tx_underrun++, restart = true;
+        if (txbd->ctrl & QUICC_BD_TX_CSL )
+            qi->tx_carrier_loss++;
+        if (txbd->ctrl & QUICC_BD_TX_HB )
+            qi->tx_heartbeat_loss++;        
+        if (txbd->ctrl & QUICC_BD_TX_DEF )
+            qi->tx_deferred++;        
+
+        if( (txbd->ctrl & QUICC_BD_TX_ERRORS) == 0 )
+            qi->tx_good++;
+
+        
         if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
+            txbd->ctrl = QUICC_BD_CTL_Wrap;
             txbd = qi->tbase;
         } else {
+            txbd->ctrl = 0;            
             txbd++;
         }
-	if (--qi->txactive == 0) {
-	  clear_led(LED_TxACTIVE);
-	}
+	qi->txactive--;
+    }
+
+    if (qi->txactive == 0) {
+        clear_led(LED_TxACTIVE);
     }
+    
     // Remember where we left off
     qi->tnext = (struct cp_bufdesc *)txbd;
+
+    if (restart)
+    {
+        quicc_eth_command(sc,QUICC_CPM_CR_RESTART_TX);
+        qi->tx_restart++;        
+    }
+    
 }
 
 //
 // Interrupt processing
 //
@@ -624,18 +787,35 @@
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info *)sc->driver_private;
     volatile struct scc_regs *scc = qi->ctl;
     unsigned short scce;
 
-    while ((scce = (scc->scc_scce & QUICC_SCCE_INTS)) != 0) {
-        if ((scce & (QUICC_SCCE_TXE | QUICC_SCCE_TX)) != 0) {
+    qi->interrupts++;
+
+    while ( (scce = scc->scc_scce) != 0 )
+    {
+        scc->scc_scce = scce;
+        
+        if ( (scce & (QUICC_SCCE_TXE | QUICC_SCCE_TX)) != 0)
+        {
             quicc_eth_TxEvent(sc, scce);
         }
-        if ((scce & QUICC_SCCE_RXF) != 0) {
+        if ( (scce & ( QUICC_SCCE_RXF | QUICC_SCCE_RX )) != 0)
+        {
             quicc_eth_RxEvent(sc);
         }
-        scc->scc_scce = scce;  // Reset the bits we handled
+        if ( (scce & QUICC_SCCE_BSY) != 0)
+        {
+            qi->rx_resource_errors++;
+        }
+        if ( (scce & QUICC_SCCE_GRC) != 0 )
+        {
+            quicc_eth_command(sc, QUICC_CPM_CR_RESTART_TX);
+            qi->tx_restart++;
+            quicc_eth_command(sc, QUICC_CPM_CR_HUNT_MODE);
+            qi->rx_restart++;
+        }
     }
 }
 
 //
 // Interrupt vector
Index: src/quicc_eth.h
===================================================================
RCS file: /cvs/ecos/ecos/packages/devs/eth/powerpc/quicc/current/src/quicc_eth.h,v
retrieving revision 1.4
diff -u -5 -r1.4 quicc_eth.h
--- src/quicc_eth.h	25 Nov 2002 23:20:51 -0000	1.4
+++ src/quicc_eth.h	14 Mar 2003 18:12:44 -0000
@@ -73,10 +73,39 @@
     struct cp_bufdesc              *tbase, *rbase;   // First Tx,Rx descriptor
     struct cp_bufdesc              *tnext, *rnext;   // Next descriptor to check for interrupt
     int                             txsize, rxsize;  // Length of individual buffers
     int                             txactive;        // Count of active Tx buffers
     unsigned long                   txkey[CYGNUM_DEVS_ETH_POWERPC_QUICC_TxNUM];
+
+    // Keep some statistics
+    cyg_uint32 interrupts;
+
+    cyg_uint32 rx_count;
+    cyg_uint32 rx_deliver;
+    cyg_uint32 rx_resource;
+    cyg_uint32 rx_restart;
+    cyg_uint32 rx_good;
+    cyg_uint32 rx_crc_errors;
+    cyg_uint32 rx_align_errors;
+    cyg_uint32 rx_resource_errors;
+    cyg_uint32 rx_overrun_errors;
+    cyg_uint32 rx_collisions;
+    cyg_uint32 rx_short_frames;
+    cyg_uint32 rx_long_frames;
+    cyg_uint32 rx_miss;
+
+    cyg_uint32 tx_count;
+    cyg_uint32 tx_complete;
+    cyg_uint32 tx_restart;
+    cyg_uint32 tx_good;
+    cyg_uint32 tx_dropped;
+    cyg_uint32 tx_underrun;
+    cyg_uint32 tx_late_collisions;
+    cyg_uint32 tx_carrier_loss;
+    cyg_uint32 tx_retransmit_error;
+    cyg_uint32 tx_heartbeat_loss;
+    cyg_uint32 tx_deferred;    
 };
 
 // SCC registers - ethernet mode
 
 // General SCC mode register
@@ -143,10 +172,15 @@
 #define QUICC_BD_RX_SH              0x0008  // Rx frame too short
 #define QUICC_BD_RX_CR              0x0004  // Bad CRC
 #define QUICC_BD_RX_OV              0x0002  // Rx overrun
 #define QUICC_BD_RX_CL              0x0001  // Collision during frame  
 
+#define QUICC_BD_RX_ERRORS          ( QUICC_BD_RX_CL | QUICC_BD_RX_OV | \
+                                      QUICC_BD_RX_CR | QUICC_BD_RX_SH | \
+                                      QUICC_BD_RX_NO | QUICC_BD_RX_LG | \
+                                      QUICC_BD_RX_MISS )
+
 // Transmit buffer status
 #define QUICC_BD_TX_PAD             0x4000  // Pad short packets
 #define QUICC_BD_TX_LAST            0x0800  // Last buffer in chain
 #define QUICC_BD_TX_TC              0x0400  // Transmit CRC after buffer
 #define QUICC_BD_TX_DEF             0x0200  // Transmission was deferred
@@ -154,10 +188,14 @@
 #define QUICC_BD_TX_LC              0x0080  // Late collision
 #define QUICC_BD_TX_RL              0x0040  // Retransmit limit exceeded
 #define QUICC_BD_TX_RC              0x003C  // Retry count
 #define QUICC_BD_TX_UN              0x0002  // Tx underrun
 #define QUICC_BD_TX_CSL             0x0001  // Carrier lost
+
+#define QUICC_BD_TX_ERRORS          (QUICC_BD_TX_CSL | QUICC_BD_TX_UN | \
+                                     QUICC_BD_TX_RL | QUICC_BD_TX_LC  | \
+                                     QUICC_BD_TX_HB | QUICC_BD_TX_DEF )
 
 #include CYGDAT_DEVS_QUICC_ETH_INL  // Platform specifics
 
 #define IEEE_8023_MAX_FRAME         1518    // Largest possible ethernet frame
 #define IEEE_8023_MIN_FRAME           64    // Smallest possible ethernet frame


-- 
Nick Garnett                    eCos Kernel Architect
http://www.ecoscentric.com/     The eCos and RedBoot experts


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]