now with attachment
> -----Original Message-----
> From: Lambrecht Jürgen
> Sent: woensdag 4 juni 2008 0:08
> To: [EMAIL PROTECTED]
> Subject: Re: [ECOS] bugs in AT91 Ethernet driver
>
> Major bug found in TX part - see below.
>
--- F:\version_ecos\packages\devs\eth\arm\at91\current\src\if_at91.c
2008-03-07 08:35:36.000000000 +-0200
+++ F:\ecos_ronetix\packages\devs\eth\arm\at91\current\src\if_at91.c
2008-06-04 00:01:51.000000000 +-0200
@@ -36,16 +36,18 @@
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): Andrew Lunn, John Eigelaar
-// Contributors:
+// Contributors: Juergen Lambrecht
// Date: 2006-05-10
-// Purpose:
-// Description:
+// Purpose: BSD compatible network driver
+// Description: HW network driver for AT91 EMAC block of AT91SAM uC's.
+// Limitations: Jumbo frames are not supported because of
+// AT91_EMAC_RBD_SR_LEN_MASK = 0xFFF
//
//####DESCRIPTIONEND####
//
//========================================================================*/
#include <pkgconf/system.h>
@@ -64,13 +66,15 @@
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#include <cyg/io/eth/eth_drv_stats.h>
-#include <cyg/io/eth_phy.h>
+#ifdef PHY_PRESENT
+ #include <cyg/io/eth_phy.h>
+#endif
#include <errno.h>
#include <string.h>
// Set up the level of debug output
#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 0
#define debug1_printf(args...) diag_printf(args)
@@ -165,13 +169,15 @@
typedef struct at91_eth_priv_s
{
cyg_uint32 intr_vector;
char *esa_key; // RedBoot 'key' for device ESA
cyg_uint8 *enaddr;
cyg_uint32 base; // Base address of device
+#ifdef PHY_PRESENT
eth_phy_access_t *phy;
+#endif
rbd_t rbd[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS];
rb_t rb[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS];
tbd_t tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS];
unsigned long curr_tx_key;
cyg_bool tx_busy;
cyg_uint32 last_tbd_idx;
@@ -297,19 +303,20 @@
{
CYG_FAIL("Unable to program MII clock");
}
HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg);
}
-
+/* The general PHY driver must know how to access the PHY. */
+#ifdef PHY_PRESENT
ETH_PHY_REG_LEVEL_ACCESS_FUNS(at91_phy,
at91_init_phy,
NULL,
at91_write_phy,
at91_read_phy);
-
+#endif
//======================================================================
// Receiver buffer handling
// Initialize the receiver buffers and descriptors
static void
at91_rb_init(at91_eth_priv_t *priv)
@@ -333,13 +340,13 @@
at91_tb_init(at91_eth_priv_t *priv)
{
int i;
for (i = 0 ; i < CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS; i++)
{
priv->tbd[i].addr = 0;
- priv->tbd[i].sr = AT91_EMAC_TBD_SR_USED;
+ priv->tbd[i].sr = 0; /* datasheet 36.4.1.3 for SAM9260 vG and 37.4.1.3
for SAM7 vG point 2 */
}
// Set the wrap bit on the last entry
priv->tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1].sr |= AT91_EMAC_TBD_SR_WRAP;
}
//======================================================================
@@ -482,13 +489,15 @@
{
struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
bool esa_ok = false;
unsigned char enaddr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR};
unsigned char enzero[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+#ifdef PHY_PRESENT
unsigned short phy_state = 0;
+#endif
cyg_uint32 ncfg = 0;
debug1_printf("\nAT91_ETH: Initialising @ %x\n",priv->base);
priv->tx_busy = false;
priv->curr_tbd_idx = 0;
@@ -518,13 +527,13 @@
4,
(cyg_addrword_t)sc,
at91_eth_isr,
eth_drv_dsr,
&priv->intr_handle,
&priv->intr);
-
+ /* default is level sensitive; */
cyg_drv_interrupt_attach(priv->intr_handle);
cyg_drv_interrupt_unmask(priv->intr_vector);
#endif
#ifdef CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA
// Get MAC address from RedBoot configuration variables
@@ -557,57 +566,64 @@
// Setup the transmit descriptors
at91_tb_init(priv);
// And tell the EMAC where the first transmit buffer descriptor is
HAL_WRITE_UINT32(priv->base + AT91_EMAC_TBQP, (cyg_uint32)priv->tbd);
+#ifdef PHY_PRESENT
// Setup the PHY
CYG_ASSERTC(priv->phy);
at91_mdio_enable();
if (!_eth_phy_init(priv->phy))
{
at91_mdio_disable();
+ debug2_printf("_eth_phy_init failed\n");
return (false);
}
// Get the current mode and print it
phy_state = _eth_phy_state(priv->phy);
+#endif
at91_mdio_disable();
HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);
-
+#ifdef PHY_PRESENT
if ((phy_state & ETH_PHY_STAT_LINK) != 0)
{
if (((phy_state & ETH_PHY_STAT_100MB) != 0))
{
debug1_printf("AT91_ETH: 100Mbyte/s");
+#endif
ncfg |= AT91_EMAC_NCFG_SPD_100Mbps;
+#ifdef PHY_PRESENT
}
else
{
debug1_printf("AT91_ETH: 10Mbyte/s");
ncfg &= ~(AT91_EMAC_NCFG_SPD_100Mbps);
}
if((phy_state & ETH_PHY_STAT_FDX))
{
debug1_printf(" Full Duplex\n");
+#endif
ncfg |= AT91_EMAC_NCFG_FD;
+#ifdef PHY_PRESENT
}
else
{
debug1_printf(" Half Duplex\n");
ncfg &= ~(AT91_EMAC_NCFG_FD);
}
}
else
{
debug1_printf("AT91_ETH: No Link\n");
}
-
+#endif
//Setup the network configuration
ncfg |= (AT91_EMAC_NCFG_RLCE);
HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);
@@ -630,31 +646,40 @@
// This function is called to stop the interface.
static void
at91_eth_stop(struct eth_drv_sc *sc)
{
at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+ cyg_uint32 bits;
+ bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND |
+ AT91_EMAC_ISR_RTRY | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM);
+
+ HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, bits);
+#endif
at91_disable(priv);
}
// This function is called to "start up" the interface. It may be called
// multiple times, even when the hardware is already running.
static void
at91_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
cyg_uint32 bits;
// Enable the interrupts we are interested in
// TODO: We probably need to add at least the RBNA interrupt here
// as well in order to do some error handling
- bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_TCOM);
+ bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND |
+ AT91_EMAC_ISR_RTRY | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM);
HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, bits);
-
+#endif
// Enable the receiver and transmitter
at91_enable(priv);
}
// This function is called for low level "control" operations
static int
@@ -697,12 +722,13 @@
//
// We allocate one buffer descriptor per scatter/gather entry. We assume that
// a typical packet will not have more than 3 such entries, and so we say we
// can send a packet when we have 3 or more buffer descriptors free
//
// TODO: Implement what the comment actually says!
+// JL: more difficult with copy to SRAM..
static int
at91_eth_can_send(struct eth_drv_sc *sc)
{
int can_send;
at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
if(priv->tx_busy)
@@ -721,20 +747,27 @@
at91_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
int i;
cyg_uint32 sr;
-
- priv->tx_busy = true;
+#ifdef SRAM1_ORIGIN /* define it in plf_io.h if present */
+ cyg_uint32 total_bytes = 0; /* position in SRAM1 */
+#endif
+ priv->tx_busy = true; /* for can_send() */
- priv->last_tbd_idx = priv->curr_tbd_idx;
+ priv->last_tbd_idx = priv->curr_tbd_idx; //??
for(i = 0;i<sg_len;i++)
{
+#ifdef SRAM1_ORIGIN
+ memcpy((cyg_uint8 *)(SRAM1_ORIGIN+total_bytes), (cyg_uint8
*)(sg_list[i].buf), sg_list[i].len);
+ priv->tbd[priv->curr_tbd_idx].addr = SRAM1_ORIGIN+total_bytes;
+#else
priv->tbd[priv->curr_tbd_idx].addr = sg_list[i].buf;
+#endif
sr = (sg_list[i].len & AT91_EMAC_TBD_SR_LEN_MASK);
// Set the End Of Frame bit in the last descriptor
if(i == (sg_len-1))
{
sr |= AT91_EMAC_TBD_SR_EOF;
@@ -747,37 +780,45 @@
}
else
{
priv->tbd[priv->curr_tbd_idx].sr = (sr | AT91_EMAC_TBD_SR_WRAP);
priv->curr_tbd_idx = 0;
}
+#ifdef SRAM1_ORIGIN
+ total_bytes += sg_list[i].len;
+#endif
}
// Store away the key for when the transmit has completed
// and we need to tell the stack which transmit has completed.
priv->curr_tx_key = key;
at91_start_transmitter(priv);
}
-static void at91_reset_tbd(at91_eth_priv_t *priv)
+/* datasheet 36.4.1.3 for SAM9260 vG and 37.4.1.3 for SAM7 vG point 2:
+ set AT91_EMAC_TBD_SR_USED=0 */
+static void at91_reset_tbd(at91_eth_priv_t *priv, bool b_reset_tbd_idx)
{
while(priv->curr_tbd_idx != priv->last_tbd_idx)
{
if(priv->last_tbd_idx == (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1))
{
- priv->tbd[priv->last_tbd_idx].sr =
- (AT91_EMAC_TBD_SR_USED|AT91_EMAC_TBD_SR_WRAP);
+ priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_WRAP;
priv->last_tbd_idx = 0;
}
else
{
- priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_USED;
+ priv->tbd[priv->last_tbd_idx].sr = 0;
priv->last_tbd_idx++;
}
- }
+ }
+ if (b_reset_tbd_idx)
+ {
+ priv->curr_tbd_idx = 0; //reset, because EMAC also resets its queue
+ } //pointer to the TBQP
}
//======================================================================
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
@@ -791,21 +832,23 @@
/* Get the interrupt status */
HAL_READ_UINT32(priv->base+AT91_EMAC_ISR,isr);
ret = CYG_ISR_HANDLED;
- //TODO: We should probably be handling some of the error interrupts as well
- if(isr & AT91_EMAC_ISR_TCOM)
+ if(isr & (AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND | AT91_EMAC_ISR_RTRY |
AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM))
{
ret = CYG_ISR_CALL_DSR;
+ debug2_printf("TX IRQ\n");
}
+ //TODO: We should probably be handling some of the error interrupts as well
if(isr & AT91_EMAC_ISR_RCOM)
{
ret = CYG_ISR_CALL_DSR;
+ debug2_printf("RX IRQ\n");
}
cyg_interrupt_acknowledge(vector);
return(ret);
}
#endif
@@ -817,56 +860,79 @@
cyg_uint32 tsr;
cyg_uint32 rsr;
cyg_uint32 ctr;
cyg_uint32 cnt;
cyg_uint32 idx;
+ bool b_reset_tbd_idx = false;
- /* Get the Transmit Status */
+ /* Get the Transmit Status and clear it */
HAL_READ_UINT32(priv->base+AT91_EMAC_TSR,tsr);
HAL_WRITE_UINT32(priv->base+AT91_EMAC_TSR,tsr);
- /* Get the Receive Status */
+ /* Get the Receive Status and clear it */
HAL_READ_UINT32(priv->base+AT91_EMAC_RSR,rsr);
HAL_WRITE_UINT32(priv->base+AT91_EMAC_RSR,rsr);
//TODO: The interrupts other than RCOMP and TCOMP needs to be
// handled properly especially stuff like RBNA which could have
// serious effects on driver performance
- /* Service the TX buffers */
+ /* Service the TX buffers after IRQ */
+ if (tsr&AT91_EMAC_TSR_OVR) //0
+ {
+ debug1_printf("AT91_ETH: Tx UBR\n");
+ b_reset_tbd_idx = true;
+ }
+
if (tsr&AT91_EMAC_TSR_COL) //1
{
debug1_printf("AT91_ETH: Tx COL\n");
}
if (tsr&AT91_EMAC_TSR_RLE) //2
{
debug1_printf("AT91_ETH: Tx RLE\n");
+ b_reset_tbd_idx = true;
}
+ if ((tsr&AT91_EMAC_TSR_TXIDLE) == 0) //3
+ {
+ debug2_printf("AT91_ETH: Tx IDLE\n");
+ }
+
if (tsr&AT91_EMAC_TSR_BNQ) //4
{
debug1_printf("AT91_ETH: Tx BEX\n");
+ b_reset_tbd_idx = true;
}
if (tsr&AT91_EMAC_TSR_UND) //6
{
debug1_printf("AT91_ETH: Tx UND\n");
+ b_reset_tbd_idx = true;
}
/* Check that the last transmission is completed */
+ /* After each transmit, it is best to immediately reset tbd */
+ /* If there were errors, tbd_idx must be reset also */
if (tsr&AT91_EMAC_TSR_COMP) //5
{
- at91_reset_tbd(priv);
+ debug2_printf("AT91_ETH: Tx COMP.\n");
+ at91_reset_tbd(priv, b_reset_tbd_idx);
_eth_drv_tx_done(sc,priv->curr_tx_key,0);
priv->tx_busy = false;
}
+ else if (b_reset_tbd_idx == true)
+ {
+ at91_reset_tbd(priv, b_reset_tbd_idx);
+ }
- /* Service the RX buffers when we get something */
+
+ /* Service the RX buffers when we get something after IRQ */
if (rsr&AT91_EMAC_RSR_REC)
{
/* Do this all until we find the first EMAC Buffer */
while (priv->rbd[priv->curr_rbd_idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW)
{
@@ -931,27 +997,30 @@
cyg_uint32 bytes_in_buffer;
cyg_uint32 bytes_in_list = 0;
cyg_uint32 bytes_needed_list = 0;
cyg_uint32 buffer_pos = 0;
cyg_uint8 * sg_buf;
cyg_uint32 total_bytes = 0;
-
+ /* buffer_pos is position in current 128B buffer */
+ /* bytes_in_list is position in current sg_list */
+ /* total_bytes is total no. of packet bytes already copied */
for(i = 0;i<sg_len;i++)
{
- while(bytes_in_list < sg_list[i].len)
- {
+ bytes_in_list = 0; /* go to next list */
+ while(bytes_in_list < sg_list[i].len) //freebsd - i=0: 14B
+ { //freebsd - i=1: 128B or remainder
of packet
bytes_needed_list = sg_list[i].len - bytes_in_list;
if(priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_EOF)
- {
- bytes_in_buffer =
+ { /* This 128B buffer contains the End Of the Frame. */
+ bytes_in_buffer =
((priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK)
- - total_bytes) - buffer_pos;
+ - total_bytes);
}
else
- {
+ { /* AT91_EMAC_RX_BUFF_SIZE = 128B */
bytes_in_buffer = AT91_EMAC_RX_BUFF_SIZE - buffer_pos;
}
sg_buf = (cyg_uint8 *)(sg_list[i].buf);
if(bytes_needed_list < bytes_in_buffer)
@@ -1001,14 +1070,17 @@
return(CYGNUM_HAL_INTERRUPT_EMAC);
}
at91_eth_priv_t at91_priv_data =
{
.intr_vector = CYGNUM_HAL_INTERRUPT_EMAC,
- .base = AT91_EMAC,
+ .base = AT91_EMAC
+#ifdef PHY_PRESENT
+ ,
.phy = &at91_phy
+#endif
};
ETH_DRV_SC(at91_sc,
&at91_priv_data, // Driver specific data
"eth0", // Name for this interface
at91_eth_start,
--
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss