This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
PPC QUICC ethernet patch
- From: Nick Garnett <nickg at ecoscentric dot com>
- To: ecos-patches at sources dot redhat dot com
- Date: 14 Mar 2003 18:18:25 +0000
- Subject: 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