public inbox for ecos-discuss@sourceware.org
 help / color / mirror / Atom feed
* [ECOS] Re: at91 emac ethernet driver lwip diff patch
       [not found] <a5143a33e2b4952478f8291234b9b4b1@haven.inspiration.ch>
@ 2015-06-10 12:12 ` Lambrecht Jürgen
  0 siblings, 0 replies; only message in thread
From: Lambrecht Jürgen @ 2015-06-10 12:12 UTC (permalink / raw)
  To: oliver.munz; +Cc: ecos-patches, ecos

[-- Attachment #1: Type: text/plain, Size: 5712 bytes --]

Hi eCos list and hi Oliver,

sorry for my late reaction..
I finally reviewed your (Oliver's) changes to my version of the 
if_at91.c driver.
And I think your changes are wrong.
I did not look at the other patches - I do not use LwIP.
For reference I added my source file in attach (it is still on my todo 
list to make it ready for commit..) and Oliver's version (cleaned up a 
bit for easier diff).

Below you find your email with my remarks, and at the end some other 
remarks.

On 23/03/15 12:16, oliver.munz wrote:
> Dear Jürgen
>
> I had problems using the driver whit lwIP. After some debuging i think
> that the driver does two things wrong:
> 1. he adds AT91_EMAC_RX_BUFF_SIZE on line 1260 if the AT91_EMAC_RBD_SR_EOF
> isn't set. But it should do it if the size is 0.
No, the '- buffer_pos' is already contained in 'total_bytes'. Else you 
count it double. Do you agree?
> 2. he dose not calculate the size the same way in at91_eth_recv() and
> at91_eth_deliver().
Yes he does.
Mark that AT91_EMAC_RBD_SR_LEN_MASK contains the length of the complete 
packet. The EMAC hardware puts that packet in 128B buffers, and only the 
last buffer can not be completely full. So for at91_eth_recv() I can 
read those 128B buffers completely, except the last buffer. But the only 
way to know how much bytes are put in that last buffer is to use the 
complete length (T91_EMAC_RBD_SR_LEN_MASK), so the number of bytes in 
the last buffer is (the-packet-length minus the-already-received-bytes).
In case of at91_eth_deliver() I am only interested in the complete size, 
no need to count it separately as sums of 128B buffers 
(AT91_EMAC_RX_BUFF_SIZE).
So I think your code has an error, because you sum all buffers and then 
finally add the complete packet length, so you have almost the double 
length?
> at91_eth_deliver() dont care about the buffers whit the
> size marked as 0.
Indeed I don't check if the AT91_EMAC_RBD_SR_LEN_MASK is valid, because 
I know (from the AT91SAM9260 datasheet - although not very clear: "If 
end of frame is not set, then the only other valid status are bits 12, 
13 and 14.") that it is only valid for the last buffer. And apparently, 
it is 0 in the other cases. And I am also confident that when the EMAC 
sets the EOF bit it has written the correct length.
> I think this leads to problems whit reciveing packeds bigger then
> 128Bytes, because the at91_eth_deliver() function don't let the ip-stack
> (at least the lwIP-stack) allocate the right amount of memory in this case.
This would mean that the length stored in bits 0-11 of the receive 
status is not correct? It should be the length of the complete packet.

Maybe there is a difference between my AT91SAM9260 and your AT91SAM7X?
Let me know if I need to send you the datasheet.


Apart from that, why didn't you use my other fixes? We already use that 
code for many years - always on the same ARM9, but on many different 
boards and many different software projects.

A remark: do you know that the AT91SAM9260 EMAC is not performant enough 
(sort of hardware bug), and that it is advised (see datasheet errata) to 
let the EMAC use the internal SRAM and not the SDRAM? Else you will have 
performance problems at full load.

My code was tested for long time under heavy loads, so you can trust 
(most of) it.
For example this remark in your code:
"   //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
"
Or for example my fix of a wrong datasheet on line 366 about 
AT91_EMAC_TBD_SR_USED in at91_tb_init().

You can use my handling of the error cases.
I also added a reset to the driver, because sometimes things went wrong 
with AT91_EMAC_ISR_TBRE (datasheet: "Transmit buffers exhausted in 
mid-frame").
Mark that that reset to the driver (with "b_reset_tbd_idx" is needed 
when running without IRQs, so for RedBoot. This because RedBoot stalls 
when e.g. you are typing data in the telnet shell, then the EMAC buffers 
are fastly exhausted.

FYI, here a link to my old eCos mails: 
http://ecos.sourceware.org/ml/ecos-discuss/2008-06/msg00016.html.


In your code in at91_eth_deliver() you use the variable 'begpack' 
(begin-of-packet).
Is there something wrong with my implementation of the recovering of the 
buffers (code with "&= ~(AT91_EMAC_RBD_ADDR_OWNER_SW") ?

Kind regards,
Jürgen

P.S.: now the code is again fresh in my head, I will try to react faster 
next time
> I did change the two funktions and now my application seems to work stable
> whitout memory holes...
>
> I would like it if You would look at it.
>
> Thanks Oliver
>
>
> On Fri, 13 Feb 2015 15:42:56 +0100, Lambrecht Jürgen
> <J.Lambrecht@TELEVIC.com> wrote:
>> On 13/02/15 13:55, oliver.munz wrote:
>>> Dear Jürgen
>>>
>>> I'm working whit eCOS and the AT91SAM7X again and i would like to get
>>> Your lateste version of the if_at91.c.
>>>
>>> Thanks
>>>
>>> Oliver Munz
>>>
>> I just took our latest file on CVS, no editing..
>> success with it - feel free to ask stuff
>> Jürgen



On 20/04/15 13:18, oliver.munz wrote:
> This is a patch for the at91 emac driver and the lwIP stack.
>
> It should solve the no PBUF leak and other stack blocking errors. It also
> replaces some "\t" whit "    "...


-- 
Jürgen Lambrecht
R&D Associate
Mobile: +32 499 644 531
Twitter: JGRLambrecht
Tel: +32 (0)51 303045    Fax: +32 (0)51 310670
http://www.televic-rail.com
Televic Rail NV - Leo Bekaertlaan 1 - 8870 Izegem - Belgium
Company number 0825.539.581 - RPR Kortrijk


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: if_at91.c --]
[-- Type: text/x-csrc; name="if_at91.c", Size: 43521 bytes --]

//==========================================================================
//
//      if_at91.c
//
//
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2006 Andrew Lunn <andrew.lunn@ascom.ch>
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Andrew Lunn, John Eigelaar, Juergen Lambrecht
// Contributors:
// Date:         2006-05-10
// 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>
#include <pkgconf/hal.h>
#include <pkgconf/devs_eth_arm_at91.h>
#include <pkgconf/io_eth_drivers.h>
#if defined(CYGPKG_REDBOOT)
   #include <pkgconf/redboot.h>
#endif

#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_diag.h>
#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>
#ifdef CYGPKG_DEVS_ETH_PHY
   #include <cyg/io/eth_phy.h>
#endif
#include <errno.h>
#include <string.h>
#ifdef TLVFUN_DEVS_ETH_ARM_FPGA_SMI
#include <cyg/hal/mii.h>
#endif


// Set up the level of debug output
#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 0
   #define debug1_printf(args...) diag_printf(args)
#else
   #define debug1_printf(args...)
#endif
#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 1
   #define debug2_printf(args...) diag_printf(args)
#else
   #define debug2_printf(args...)
#endif

//Driver interface callbacks
#define _eth_drv_init(sc,mac)         \
  (sc->funs->eth_drv->init)(sc,(unsigned char *)mac)
#define _eth_drv_tx_done(sc,key,status)      \
  (sc->funs->eth_drv->tx_done)(sc,key,status)
#define _eth_drv_recv(sc,len)         \
  (sc->funs->eth_drv->recv)(sc,len)

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
static cyg_uint32
at91_eth_isr (cyg_vector_t vector, cyg_addrword_t data);
#endif

// --------------------------------------------------------------
// RedBoot configuration options for managing ESAs for us

// Decide whether to have redboot config vars for it...
#if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGPKG_REDBOOT_NETWORKING)
   #include <redboot.h>
   #include <flash_config.h>

   #ifdef CYGSEM_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA_ETH0
RedBoot_config_option("Network hardware address [MAC] for eth0",
                      eth0_esa_data,
                      ALWAYS_ENABLED, true,
                      CONFIG_ESA, 0);
   #endif

#endif  // CYGPKG_REDBOOT_NETWORKING && CYGSEM_REDBOOT_FLASH_CONFIG

// and initialization code to read them
// - independent of whether we are building RedBoot right now:
//#ifdef CYGPKG_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA
//the application has to provide the flash config access function; if not, a
//NOP will be executed, so also ok.
#include <cyg/hal/hal_if.h>
#if defined (CYGPKG_IO_REDBOOT_VVT)
#include <cyg/io/flash_config_vvt.h>
#endif
#ifndef CONFIG_ESA
#define CONFIG_ESA (6)
#endif

/*   #define CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA( mac_address, ok )           \ */
/*   CYG_MACRO_START                                                       \ */
/*   ok = CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET,       \ */
/*                                     "eth0_esa_data",                    \ */
/*                                     mac_address,                        \ */
/*                                     CONFIG_ESA);                        \ */
/*   CYG_MACRO_END */
/*         struct cyg_fconfig info; */
/*     info.key = "eth0_esa_data"; */
/*     info.val = (void *)&enaddr[0]; */
/*     info.type = CONFIG_ESA; */
/*     info.offset = 0; */
/*     esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP2(CYGNUM_CALL_IF_FLASH_CFG_GET, &info); */

//#endif // CYGPKG_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA

//============================================================================

// Private Data structures

#ifndef AT91_EMAC_RX_BUFF_SIZE
#define AT91_EMAC_RX_BUFF_SIZE  128
#endif

// Receive Buffer Descriptor
typedef struct rbd_s
{
   cyg_uint32 addr;
   cyg_uint32 sr;
} rbd_t;

// Receive Buffer
typedef struct rb_s
{
   cyg_uint8 rb[AT91_EMAC_RX_BUFF_SIZE];
} rb_t;

// Transmit Buffer Descriptor
typedef struct tbd_s
{
   cyg_uint32 addr;
   cyg_uint32 sr;
} tbd_t;

// AT91 Ethernet private data
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 CYGPKG_DEVS_ETH_PHY
   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] __attribute__ ((aligned (16)));
   tbd_t tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS];
   unsigned long curr_tx_key;
   cyg_bool tx_busy;
   cyg_uint32 last_tbd_idx;
   cyg_uint32 curr_tbd_idx;
   cyg_uint32 curr_rbd_idx;
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   cyg_interrupt intr;
   cyg_handle_t  intr_handle;
   cyg_uint32    isr; //to inform deliver() of calling IRQ
#endif
#ifndef CYGPKG_REDBOOT
   struct ether_drv_stats ifStats;
#endif
} at91_eth_priv_t;

#ifdef CYGPKG_DEVS_ETH_PHY
//============================================================================
// PHY access bits and pieces
//

static void
at91_mdio_enable(void)
{
   cyg_uint32 val;
   HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
   val |= AT91_EMAC_NCR_MPE;    /* enable management port */
   HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
}

static void
at91_mdio_disable(void)
{
   cyg_uint32 val;
   HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
   val &= ~AT91_EMAC_NCR_MPE;    /* disable management port */
   HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
}

// Write one of the PHY registers via the MII bus
void
at91_write_phy(int reg_addr, int phy_addr, unsigned short data)
{
   cyg_uint32 val, cnt=0;

   CYG_ASSERTC(reg_addr >= 0 && reg_addr <= AT91_EMAC_MAN_REGA_MASK);
   CYG_ASSERTC(phy_addr >= 0 && phy_addr <= AT91_EMAC_MAN_PHY_MASK);

   val = (AT91_EMAC_MAN_SOF  |
         AT91_EMAC_MAN_WR   |
         AT91_EMAC_MAN_CODE |
         AT91_EMAC_MAN_PHYA(phy_addr) |
         AT91_EMAC_MAN_REGA(reg_addr) |
         AT91_EMAC_MAN_DATA(data));

   HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_MAN, val);

   /* Wait until IDLE bit in Network Status register is cleared */
   while (cnt < 1000000)
   {
      HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val);
      if (!(val & AT91_EMAC_NSR_IDLE))
         break;
   }
   CYG_ASSERTC(cnt < 1000000);
   debug2_printf("at91_write_phy 0x%X 0x%X 0x%X\n", (unsigned int)reg_addr, (unsigned int)phy_addr, (unsigned int)data);
}


// Read one of the PHY registers via the MII bus
bool
at91_read_phy(int reg_addr, int phy_addr, unsigned short *data)
{
   cyg_uint32 val;

   CYG_ASSERTC(reg_addr >= 0 && reg_addr <= AT91_EMAC_MAN_REGA_MASK);
   CYG_ASSERTC(phy_addr >= 0 && phy_addr <= AT91_EMAC_MAN_PHY_MASK);

   val = (AT91_EMAC_MAN_SOF  |
         AT91_EMAC_MAN_RD   |
         AT91_EMAC_MAN_CODE |
         AT91_EMAC_MAN_PHYA(phy_addr) |
         AT91_EMAC_MAN_REGA(reg_addr));


   HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_MAN, val);
   /* Wait until IDLE bit in Network Status register is cleared */
   do
   {
      HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val);
   }while(val & AT91_EMAC_NSR_IDLE);

   HAL_DELAY_US(50);

   HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_MAN, val);
   *data = val & AT91_EMAC_MAN_DATA_MASK;
   debug2_printf("at91_read_phy 0x%X 0x%X 0x%X\n", (unsigned int)reg_addr, (unsigned int)phy_addr, (unsigned int)(*data));

   return (true);
}

// Enable the MDIO bit in MAC control register so that we can talk to
// the PHY. Also set the clock divider so that MDC is less than 2.5MHz.
static void
at91_init_phy(void)
{
   cyg_uint32 cfg;
   cyg_uint32 div;

   HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg);
   cfg &=~ AT91_EMAC_NCFG_CLK_MASK;

   div = (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / 2500000);
   if (div < 8)
   {
      cfg |= AT91_EMAC_NCFG_CLK_HCLK_8;
   }
   else if (div < 16)
   {
      cfg |= AT91_EMAC_NCFG_CLK_HCLK_16;
   }
   else if (div < 32)
   {
      cfg |= AT91_EMAC_NCFG_CLK_HCLK_32;
   }
   else if (div < 64)
   {
      cfg |= AT91_EMAC_NCFG_CLK_HCLK_64;
   }
   else
   {
      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. */
ETH_PHY_REG_LEVEL_ACCESS_FUNS(at91_phy,
                              at91_init_phy,
                              NULL,
                              at91_write_phy,
                              at91_read_phy);
#endif // PHY access bits and pieces
//======================================================================
// Receiver buffer handling

// Initialize the receiver buffers and descriptors
static void
at91_rb_init(at91_eth_priv_t *priv)
{
   int i;
   debug1_printf("RX buf base address = 0x%X\n", ((cyg_uint32)&priv->rb[0]));
   for (i = 0 ; i < CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; i++)
   {
      priv->rbd[i].addr = ((cyg_uint32)&priv->rb[i]) & AT91_EMAC_RBD_ADDR_MASK;
      priv->rbd[i].sr = 0;
   }
   // Set the wrap bit on the last entry
   priv->rbd[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS-1].addr |=
     AT91_EMAC_RBD_ADDR_WRAP;
}

//======================================================================
// Transmit buffer handling

// Initialize the transmit buffer descriptors
static void
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 = 0; /* AT91_EMAC_TBD_SR_USED; */ /* datasheet is wrong! 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;
}

//======================================================================
// Enable and Disable of the receiver and transmitter.

static void
at91_disable_rx(at91_eth_priv_t *priv)
{
   cyg_uint32 ctl;

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
   ctl &= ~AT91_EMAC_NCR_RE;
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void
at91_disable_tx(at91_eth_priv_t *priv)
{
   cyg_uint32 ctl;

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
   ctl &= ~AT91_EMAC_NCR_TX;
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void
at91_enable_rx(at91_eth_priv_t *priv)
{
   cyg_uint32 ctl;

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
   ctl |= AT91_EMAC_NCR_RE;
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void
at91_enable_tx(at91_eth_priv_t *priv)
{
   cyg_uint32 ctl;

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
   ctl |= AT91_EMAC_NCR_TX;
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void
at91_enable(at91_eth_priv_t *priv)
{
   at91_enable_tx(priv);
   at91_enable_rx(priv);
}

static void
at91_disable(at91_eth_priv_t *priv)
{
   at91_disable_tx(priv);
   at91_disable_rx(priv);
}

static void
at91_start_transmitter(at91_eth_priv_t *priv)
{
   cyg_uint32 ctl;

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
   ctl |= AT91_EMAC_NCR_TSTART;
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}


//======================================================================
// Initialization code

// Configure the pins so that the EMAC has control of them. This
// assumes the MII is used, not the RMII
static void
at91_cfg_pins(void)
{
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EREFCK);
#ifdef CYGPKG_DEVS_ETH_PHY
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ECRS);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ECOL);
#endif
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXDV);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX0);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX1);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX2);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX3);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXER);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXCK);

   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETXEN);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX0);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX1);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX2);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX3);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETXER);

#ifdef CYGPKG_DEVS_ETH_PHY
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDC);
   HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDIO);
#endif
}

// Set a specific address match to a given address. Packets received which
// match this address will be passed on.
static void
at91_set_mac(at91_eth_priv_t * priv, cyg_uint8 * enaddr, int sa)
{
   cyg_uint32 hi, lo;

   CYG_ASSERTC(sa > 0 && sa < 5);
   sa--;

   lo = ((enaddr[3] << 24) |
         (enaddr[2] << 16) |
         (enaddr[1] <<  8) |
         (enaddr[0]));

   hi = ((enaddr[5] <<  8) |
         (enaddr[4]));

   HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1L + (8*sa), lo);
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1H + (8*sa), hi);
}

static void
at91_clear_stats(at91_eth_priv_t *priv)
{
   cyg_uint32 ctl;

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
   ctl |= AT91_EMAC_NCR_CSR;
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

// Enable and Disable of the receiver and transmitter.
// Initialize the interface. This configures the interface ready for use.
// Interrupts are grabbed etc. This means the start function has
// little to do except enable the receiver
static bool
at91_eth_init(struct cyg_netdevtab_entry *tab)
{
   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 CYGPKG_DEVS_ETH_PHY
   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;
   priv->curr_rbd_idx = 0;
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   priv->isr = 0;
#endif
   // Enable the clock to the EMAC - TODO: is it needed to enable PIO clk??
   HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_EMAC);
/*    HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_PIOB); */
/*    HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_PIOA); */

   //at91_disable(priv);
   at91_cfg_pins();

   /* Enable  IO Clock */
   HAL_WRITE_UINT32(priv->base+AT91_EMAC_USRIO,AT91_EMAC_USRIO_CLKEN);

   /* Disable all the interrupts for the moment            */
   /* The Start function actually enables all that we need */
   //HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, 0x3FFF);

   // If we are building an interrupt enabled version, install the
   // interrupt handler
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   debug1_printf("AT91_ETH: Installing Interrupts on IRQ %d\n",
       priv->intr_vector);
   cyg_drv_interrupt_create(priv->intr_vector,
                            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
/*    CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA(&enaddr[0], esa_ok); */
   esa_ok = flash_get_config("eth0_esa_data", &enaddr[0], CONFIG_ESA);
   // If this call fails myMacAddr is unchanged and MAC address from
   // CDL is used
/* #endif */

   if (!esa_ok)
   {
      // Can't figure out ESA
      debug1_printf("AT91_ETH - Warning! ESA unknown\n");
   }
   debug1_printf("AT91_ETH: init %02x:%02x:%02x:%02x:%02x:%02x\n",
                 enaddr[0],enaddr[1],enaddr[2],
                 enaddr[3],enaddr[4],enaddr[5]);

   // Give the EMAC its address
   at91_set_mac(priv, enaddr, 1);
   at91_set_mac(priv, enzero, 2);
   at91_set_mac(priv, enzero, 3);
   at91_set_mac(priv, enzero, 4);

   // Setup the receiver buffers and descriptors
   at91_rb_init(priv);

   // And tell the EMAC where the first receive buffer descriptor is
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_RBQP, (cyg_uint32)priv->rbd);

   // 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 CYGPKG_DEVS_ETH_PHY
   // Setup the PHY
   CYG_ASSERTC(priv->phy);

   at91_mdio_enable();
   if (!_eth_phy_init(priv->phy))
   {
      at91_mdio_disable();
      debug1_printf("_eth_phy_init failed\n");
      return (false);
   }
   else {debug1_printf("_eth_phy_init succeeded\n");}
   // Get the current mode and print it
   phy_state = _eth_phy_state(priv->phy);
   //at91_mdio_disable(); JL: why disable??
#endif

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);

#ifdef CYGPKG_DEVS_ETH_PHY
   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 CYGPKG_DEVS_ETH_PHY
      }
      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 CYGPKG_DEVS_ETH_PHY
      }
      else
      {
         debug1_printf(" Half Duplex\n");
         ncfg &= ~(AT91_EMAC_NCFG_FD);
      }
   }
   else
   {
      debug1_printf("AT91_ETH: No Link\n");
   }
#endif

#if CYGINT_IO_ETH_MULTICAST
   /* Enable multicast */
   ncfg |= AT91_EMAC_NCFG_MTI;
#endif
   //Setup the network configuration
   ncfg |= (AT91_EMAC_NCFG_RLCE);

   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);

   // Clear the Statistics counters;
   at91_clear_stats(priv);


   /* Clear the status registers */
   HAL_READ_UINT32(priv->base + AT91_EMAC_ISR,ncfg);
   HAL_READ_UINT32(priv->base + AT91_EMAC_RSR,ncfg);
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_RSR,ncfg);
   HAL_READ_UINT32(priv->base + AT91_EMAC_TSR,ncfg);
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_TSR,ncfg);

   // Initialize the upper layer driver
   _eth_drv_init(sc,enaddr);

   return (true);
}

// 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_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP |
           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
/* The following errors only cause the internal counter/pointer to be reset if
   also bit 6 TXERR is set: AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND
   |AT91_EMAC_ISR_RTRY |
   The datasheet is not clear about this... */
   bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP |
           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
at91_eth_control(struct eth_drv_sc *sc, unsigned long key,
                 void *data, int length)
{
   at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
   switch (key)
   {
#ifdef TLVFUN_DEVS_ETH_ARM_FPGA_SMI
    //the defines below are only valid if CYGPKG_NET, so not for RedBoot
    // to use statistics, look at intel/i82559/../if_i82559.c
   case ETH_DRV_GET_IF_STATS_UD:
   case ETH_DRV_GET_IF_STATS:
     {
       struct ether_drv_stats *p = (struct ether_drv_stats*)data;
       cyg_uint16 linkStatus;
       ReadMII(ETH0_SMI_ADDR, 0x00, &linkStatus);
       if (linkStatus & PhyStatus_Resolved)
         {
           priv->ifStats.resolved = 3;
           if (linkStatus & PhyStatus_FullDuplex)
             priv->ifStats.duplex = 3;
           else
             priv->ifStats.duplex = 2;

           if (linkStatus & PhyStatus_100Mb)
             priv->ifStats.speed = 100000000;
           else
             priv->ifStats.speed = 10000000;
         }
       else
         {
           priv->ifStats.resolved = 2;
           priv->ifStats.duplex = 1;
         }
       if (linkStatus & PhyStatus_LinkUp)
         priv->ifStats.operational = 3;
       else
         priv->ifStats.operational = 2;

       *p = priv->ifStats;
       return 0;
     }
#else
#ifndef CYGPKG_REDBOOT
     /* Default we assume the link is up.
    * Eg: DVA board
    * Needed for DHCP */
   case ETH_DRV_GET_IF_STATS_UD:
   case ETH_DRV_GET_IF_STATS:
   {
     struct ether_drv_stats *p = (struct ether_drv_stats*)data;
     priv->ifStats.resolved = 3;
     priv->ifStats.duplex = 3; /* Full duplex */
     priv->ifStats.speed = 100000000; /* 100 Mbit */
     priv->ifStats.operational = 3;
     *p = priv->ifStats;
     return 0;
   }
#endif
#endif
      case ETH_DRV_SET_MAC_ADDRESS:
         {
            if(length >= ETHER_ADDR_LEN)
            {
               at91_eth_stop(sc);

               cyg_uint8 * enaddr = (cyg_uint8 *)data;
               debug1_printf("AT91_ETH: set %02x:%02x:%02x:%02x:%02x:%02x\n",
                             enaddr[0],enaddr[1],enaddr[2],
                             enaddr[3],enaddr[4],enaddr[5]);

               at91_set_mac((at91_eth_priv_t *)sc->driver_private,enaddr,1);
               at91_eth_start(sc,enaddr,0);
               return 0;
            }
            return 1;
         }
#if CYGINT_IO_ETH_MULTICAST
    case ETH_DRV_SET_MC_ALL:
    {
        HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRB,0xFFFFFFFF);
        HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRT,0xFFFFFFFF);
      return  0;
    }
    case ETH_DRV_SET_MC_LIST:
    {
        HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRB,0xFFFFFFFF);
        HAL_WRITE_UINT32(priv->base+AT91_EMAC_HRT,0xFFFFFFFF);
      return  0;
      /*
      struct eth_drv_mc_list* mcl = (struct eth_drv_mc_list*) data;
      for (i=0; i<mcl->len; i++)
      {
        //mcl->addrs[0][0]==0x01; mcl->addrs[0][1]==0x00; mcl->addrs[0][2]==0x5E;
        stdims_write(eth, FPGA_MC_OFFSET+4*i, ((mcl->addrs[i][4]<<8)|(mcl->addrs[i][5])));
        stdims_write(eth, FPGA_MC_OFFSET+2+4*i, ((mcl->addrs[i][2]<<8)|(mcl->addrs[i][3])));
      }
      for (i=mcl->len; i<ETH_DRV_MAX_MC; i++)
      {
        stdims_write(eth, FPGA_MC_OFFSET+4*i, 0);
        stdims_write(eth, FPGA_MC_OFFSET+2+4*i, 0);
      }
      //read back MC address; if fail, return 1 (MC is available, but failed this time)
      */
      return  0;
    }
#else
    case ETH_DRV_SET_MC_ALL:
    case ETH_DRV_SET_MC_LIST:
      return 0;
      break;

#endif
      default:
         {
            diag_printf("%s.%d: key %lx\n", __FUNCTION__, __LINE__, key);
            return (1);
         }
   }

}

// This function is called to see if another packet can be sent.
// It should return the number of packets which can be handled.
// Zero should be returned if the interface is busy and can not send
// any more.
//
// 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)
   {
      can_send = 0;
   }
   else
   {
      can_send = 1;
   }
   return (can_send);
}

// This routine is called to send data to the hardware
static void
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;
#ifdef SRAM1_ORIGIN /* define it in plf_io.h if present */
   cyg_uint32 total_bytes = 0; /* position in SRAM1 */
   //TODO: this code abuses that the transmit buffer only contains 1 packet
   //because can_send() returns false as long as the only-one buffer is not cleared
#endif
   priv->tx_busy = true; /* for can_send() */

   priv->last_tbd_idx = priv->curr_tbd_idx;

   for(i = 0;i<sg_len;i++)
   {
#ifdef SRAM1_ORIGIN
     //todo: scheduler_lock()
      memcpy((cyg_uint8 *)(SRAM1_ORIGIN+total_bytes), (cyg_uint8 *)(sg_list[i].buf), sg_list[i].len);

      total_bytes += sg_list[i].len;
   } /* When using SRAM, use 1 buffer per packet */
   priv->tbd[priv->curr_tbd_idx].addr = SRAM1_ORIGIN;
   sr = (total_bytes & AT91_EMAC_TBD_SR_LEN_MASK); //this clears also used bit
   sr |= AT91_EMAC_TBD_SR_EOF;
#else
      priv->tbd[priv->curr_tbd_idx].addr = sg_list[i].buf;

      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;
      }
#endif
/*  TODO: add this again to use a circular buffer for transmit */
/*       if(priv->curr_tbd_idx < (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1)) */
/*       { */
/*          priv->tbd[priv->curr_tbd_idx].sr = sr; */
/*          priv->curr_tbd_idx++; */
/*       } */
/*       else */
/*       { */
         priv->tbd[priv->curr_tbd_idx].sr = (sr | AT91_EMAC_TBD_SR_WRAP);
/*          priv->curr_tbd_idx = 0; */
/*       } */
#ifndef SRAM1_ORIGIN
   } /* When NOT using SRAM, use sg_list buffers */
#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);
}

/* 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)
{
/*    cyg_uint32 tbd_idx; */

   priv->tbd[0].sr = AT91_EMAC_TBD_SR_WRAP;
/*    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_WRAP; */
/*        priv->last_tbd_idx = 0; */
/*      } */
/*      else */
/*      { */
/*        priv->tbd[priv->last_tbd_idx].sr = 0; */
/*        priv->last_tbd_idx++; */
/*      } */
/*    } */
/*    HAL_READ_UINT32(priv->base+AT91_EMAC_TBQP,tbd_idx); //synchronize */
/* *    priv->last_tbd_idx = priv->curr_tbd_idx =  * */
/*    tbd_idx -= (cyg_uint32)priv->tbd; */
/*    debug2_printf("tbd_idx: %X\n", priv->curr_tbd_idx); */
/*    if (b_reset_tbd_idx) */
/*    { */
/*       priv->curr_tbd_idx = 0; //reset, because EMAC also resets its queue */
/*    }  //pointer to the TBQP. No need to reset last_tbd_idx: is each time reinitialized */
/*    assert(tbd_idx==priv->curr_tbd_idx); */
}


//======================================================================

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
static cyg_uint32
at91_eth_isr (cyg_vector_t vector, cyg_addrword_t data)
{
   struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
   at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
   cyg_uint32 ret;
   cyg_uint32 isr;

   /* Get the interrupt status */
   HAL_READ_UINT32(priv->base+AT91_EMAC_ISR,isr);
   priv->isr |= isr; //could be several irqs before the dsr is run

   ret = CYG_ISR_HANDLED;

/* The following errors only cause the internal counter/pointer to be reset if
   also bit 6 TXERR is set: AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND
   |AT91_EMAC_ISR_RTRY |
   The datasheet is not clear about this... */
   if(isr & (AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM))
   {
      ret = CYG_ISR_CALL_DSR;
/*       debug2_printf("TX IRQ\n"); */
   }

   if(isr & (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP))
   {
      ret = CYG_ISR_CALL_DSR;
/*       debug2_printf("RX IRQ\n"); */
   }
   cyg_interrupt_acknowledge(vector);
   return(ret);
}
#endif

static void
at91_eth_deliver(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 isr;
#endif
   cyg_uint32 tsr=0;
   cyg_uint32 rsr=0;

   cyg_uint32 ctr;
   cyg_uint32 cnt;
   cyg_uint32 idx;
   bool b_reset_tbd_idx = false;

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   cyg_interrupt_disable();
   isr = priv->isr;
   priv->isr = 0;
   cyg_interrupt_enable();
   debug2_printf("local IRQ %X\n", isr);
#else
   debug2_printf("AT91_ETH in\n");
#endif
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   if (isr & (AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM))
   {
#endif
   /* 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);
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   }
   if(isr & (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP))
   {
#endif
   /* 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);
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   }
#endif

   /* Service the TX buffers after IRQ */
   if (tsr&AT91_EMAC_TSR_OVR)  //0
   {
      debug2_printf("AT91_ETH: Tx UBR\n");
   }

   if (tsr&AT91_EMAC_TSR_COL)  //1
   {
      debug2_printf("AT91_ETH: Tx COL\n");
   }

   if (tsr&AT91_EMAC_TSR_RLE)  //2
   {
      debug1_printf("AT91_ETH: Tx RLE\n");
#ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
      b_reset_tbd_idx = true;
#endif
   }

   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");
#ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
      b_reset_tbd_idx = true;
#endif
   }

   if (tsr&AT91_EMAC_TSR_UND)  //6
   {
      debug1_printf("AT91_ETH: Tx UND\n");
#ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
      b_reset_tbd_idx = true;
#endif
   }

   /* Check that the last transmission is completed */
   if (tsr&AT91_EMAC_TSR_COMP) //5
   {
      debug2_printf("AT91_ETH: Tx COMP\n");
   }

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   b_reset_tbd_idx = (isr & AT91_EMAC_ISR_TBRE);
#endif
   /* After each transmit, it is best to immediately reset tbd */
   /* If there were errors, tbd_idx must be reset also */
   //Do it always: if already done, curr==last; synchronization stays
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   if (isr & (AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM))
#else
   if ((tsr&AT91_EMAC_TSR_COMP) || (b_reset_tbd_idx == true)) //5
#endif
   {
      at91_reset_tbd(priv, b_reset_tbd_idx);
      priv->tx_busy = false;
      _eth_drv_tx_done(sc,priv->curr_tx_key,0);
   }


   /* Check for receive errors, and fix them if needed/possible. */
   if (rsr&AT91_EMAC_RSR_OVR)
   {
      debug1_printf("AT91_ETH: Rx OVR\n");
   }
   if (rsr&AT91_EMAC_RSR_BNA)
   { /* clear all used bits; something went wrong, or we're not fast enough.. */
      debug1_printf("AT91_ETH: Rx BNA\n");
      for (idx=0; idx<CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; idx++)
      {
         priv->rbd[idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
      }
      return;
   }

   /* Service the RX buffers when we get something after IRQ */
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
   if (isr & (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_ROVR | AT91_EMAC_ISR_HRESP))
#else
   if (rsr& (AT91_EMAC_RSR_REC|AT91_EMAC_RSR_OVR))
#endif
     { /* at OVR, only the last buffer is recover by the EMAC; SW should
          recover the other ones */
#if 0
      cyg_uint32 rbd_idx; /* Enable this if you don't trust the hardware */
#endif
      debug2_printf("AT91_ETH: Rx COMP\n");
      /* Do this all until we find the first EMAC Buffer */
      while (priv->rbd[priv->curr_rbd_idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW)
      {

         //Firstly walk through to either the first buffer that belongs
         // to the controller or the first SOF
         while ((priv->rbd[priv->curr_rbd_idx].addr &
                 AT91_EMAC_RBD_ADDR_OWNER_SW) &&
                !(priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_SOF))
         {
            priv->rbd[priv->curr_rbd_idx].addr &=
              ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
            priv->curr_rbd_idx++;
            if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)
            {
               priv->curr_rbd_idx = 0;
            }
#if 0
            /* check if we're not accessing same buffer as EMAC HW */
            HAL_READ_UINT32(priv->base+AT91_EMAC_RBQP,rbd_idx);
            rbd_idx &= ~0x1; /* counts per 2 so last bit=0 */
            rbd_idx -= (cyg_uint32)priv->rbd; /* we want the counter, not the address */
            if (rbd_idx == priv->curr_rbd_idx)
            {
              debug1_printf("AT91_ETH: sw catched hw!\n");
              return; //how to break; out of 2 loops?
            }
#endif
         }
         /* Check that we did find a SOF (and not an OWNER_SW=0) */
         if ((priv->rbd[priv->curr_rbd_idx].addr &
              AT91_EMAC_RBD_ADDR_OWNER_SW) &&
             (priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_SOF))
         {
            ctr = 0;
            idx = priv->curr_rbd_idx;
            /* Look for an EOF as long as the SW owns the buffer */
            while (priv->rbd[idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW)
/*             for (ctr=0;ctr<CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS;ctr++) too much
               effort: not needed to check all buffers, only until not owner anymore*/
            {
#if 0
               /* check if we're not accessing same buffer as EMAC HW */
               HAL_READ_UINT32(priv->base+AT91_EMAC_RBQP,rbd_idx);
               rbd_idx &= ~0x1; /* counts per 2 so last bit=0 */
               rbd_idx -= (cyg_uint32)priv->rbd; /* we want the counter, not the address */
               if (rbd_idx == idx)
               {
                 debug1_printf("AT91_ETH: SW catched HW!\n");
                 return; //how to break; out of 2 loops?
               }
#endif
               /* check if not again a SOF */
               if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_SOF)
               {  /* recover buffers */
                  while (priv->curr_rbd_idx != idx)
                  {
                    priv->rbd[priv->curr_rbd_idx].addr &=
                      ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
                    priv->curr_rbd_idx++;
                    if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)
                    {
                       priv->curr_rbd_idx = 0;
                    }
                  }
               }
               /* look for EOF */
               if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_EOF)
               {  /* only for last buffer the length is valid */
                  cnt = (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_LEN_MASK);
                  /* The recv function will adjust the current buffer idx
                     after the buffer has been cleared
                   */
                  if (cnt) {
                     _eth_drv_recv(sc,cnt);} /* TODO: this function can fail, and
                             not call local recv, so not clear the OWNER_SW bit */
                  else
                  {  /* recover buffers */
                     debug1_printf("AT91_ETH: Rx 0 B?\n");
                     while (priv->curr_rbd_idx != idx)
                     {
                        priv->rbd[priv->curr_rbd_idx].addr &=
                          ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
                        priv->curr_rbd_idx++;
                        if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)
                        {
                           priv->curr_rbd_idx = 0;
                        }
                     }
                  }
                  debug2_printf("AT91_ETH: Rx data\n");
                  break;
               }
               ctr++;
               idx = (ctr+priv->curr_rbd_idx)%CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS;
            }
         }
      }
   }
   debug2_printf("AT91_ETH out\n");
}

static void
at91_eth_recv(struct eth_drv_sc *sc,
              struct eth_drv_sg *sg_list,
              int sg_len)
{
   at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
   int i;
   cyg_uint32 bytes_in_buffer;       /* # not yet copied bytes in EMAC buffer */
   cyg_uint32 bytes_in_list = 0;     /* position in current sg_list */
   cyg_uint32 bytes_needed_list = 0; /* # bytes still free in sg_list */
   cyg_uint32 buffer_pos = 0;        /* position in current EMAC buffer */
   cyg_uint8 * sg_buf;
   cyg_uint32 total_bytes = 0;       /* total no. of packet bytes already copied */

   for(i = 0;i<sg_len;i++)
   {
      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)
         { /* This 128B buffer contains the End Of the Frame. */
           /* AT91_EMAC_RBD_SR_LEN_MASK contains Length of frame including FCS (if selected) */
            bytes_in_buffer =
              (priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK)
              - total_bytes; /* Not '- buffer_pos' because that is already contained in 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)
         {
            if(sg_buf != NULL)
               memcpy(&sg_buf[bytes_in_list],
                      &priv->rb[priv->curr_rbd_idx].rb[buffer_pos],
                      bytes_needed_list);
            bytes_in_list += bytes_needed_list;
            buffer_pos += bytes_needed_list;
            total_bytes += bytes_needed_list;
         }
         else /* all data from EMAC buffer can be copied */
         { /* also here for last bytes of packet: sg_len[last].len matches */
            if(sg_buf != NULL)
               memcpy(&sg_buf[bytes_in_list],
                      &priv->rb[priv->curr_rbd_idx].rb[buffer_pos],
                      bytes_in_buffer);
            bytes_in_list += bytes_in_buffer;
            total_bytes += bytes_in_buffer;

            /* Step our buffer on one */
            priv->rbd[priv->curr_rbd_idx].addr &=
              ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
            priv->curr_rbd_idx++;
            if(priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)
            {
               priv->curr_rbd_idx = 0;
            }
            buffer_pos = 0;
         }
      }
   }
}

// routine called to handle ethernet controller in polled mode
static void
at91_eth_poll(struct eth_drv_sc *sc)
{
   /* Service the buffers */
   at91_eth_deliver(sc);
}

static int
at91_eth_int_vector(struct eth_drv_sc *sc)
{
   return(CYGNUM_HAL_INTERRUPT_EMAC);
}

at91_eth_priv_t at91_priv_data =
{
   .intr_vector = CYGNUM_HAL_INTERRUPT_EMAC,
   .base = AT91_EMAC
#ifdef CYGPKG_DEVS_ETH_PHY
   ,
   .phy = &at91_phy
#endif
};

ETH_DRV_SC(at91_sc,
           &at91_priv_data,       // Driver specific data
           "eth0",                // Name for this interface
           at91_eth_start,
           at91_eth_stop,
           at91_eth_control,
           at91_eth_can_send,
           at91_eth_send,
           at91_eth_recv,
           at91_eth_deliver,
           at91_eth_poll,
           at91_eth_int_vector);

NETDEVTAB_ENTRY(at91_netdev,
                "at91",
                at91_eth_init,
                &at91_sc);

// EOF if_at91.c

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: if_at91_OliverM_LwIP.c --]
[-- Type: text/x-csrc; name="if_at91_OliverM_LwIP.c", Size: 29078 bytes --]

//==========================================================================
//
//      if_at91.c
//
//
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2010 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Andrew Lunn, John Eigelaar
// Contributors:  OLI
// Date:         2015-03-16
// Purpose:
// Description:
//
//####DESCRIPTIONEND####
//
//========================================================================*/

#include <pkgconf/system.h>
#include <pkgconf/hal.h>
#include <pkgconf/devs_eth_arm_at91.h>
#include <pkgconf/io_eth_drivers.h>
#if defined(CYGPKG_REDBOOT)
#include <pkgconf/redboot.h>
#endif

#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_diag.h>
#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>
#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)
#else
#define debug1_printf(args...)
#endif
#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 1
#define debug2_printf(args...) diag_printf(args)
#else
#define debug2_printf(args...)
#endif

//Driver interface callbacks
#define _eth_drv_init(sc,mac)			\
  (sc->funs->eth_drv->init)(sc,(unsigned char *)mac)
#define _eth_drv_tx_done(sc,key,status)		\
  (sc->funs->eth_drv->tx_done)(sc,key,status) 
#define _eth_drv_recv(sc,len)			\
  (sc->funs->eth_drv->recv)(sc,len) 

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
static cyg_uint32
at91_eth_isr(cyg_vector_t vector, cyg_addrword_t data);
#endif

// --------------------------------------------------------------
// RedBoot configuration options for managing ESAs for us

// Decide whether to have redboot config vars for it...
#if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && defined(CYGPKG_REDBOOT_NETWORKING)
#include <redboot.h>
#include <flash_config.h>

#ifdef CYGSEM_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA_ETH0
RedBoot_config_option("Network hardware address [MAC] for eth0",
		eth0_esa_data,
		ALWAYS_ENABLED, true,
		CONFIG_ESA, 0);
#endif

#endif  // CYGPKG_REDBOOT_NETWORKING && CYGSEM_REDBOOT_FLASH_CONFIG

// and initialization code to read them
// - independent of whether we are building RedBoot right now:
#ifdef CYGPKG_DEVS_ETH_ARM_AT91_REDBOOT_HOLDS_ESA

#include <cyg/hal/hal_if.h>

#ifndef CONFIG_ESA
#define CONFIG_ESA (6)
#endif

#define CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA( mac_address, ok )           \
  CYG_MACRO_START                                                       \
  ok = CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET,       \
                                    "eth0_esa_data",                    \
                                    mac_address,                        \
                                    CONFIG_ESA);                        \
  CYG_MACRO_END

#endif // CYGPKG_DEVS_ETH_AT91_ETH_REDBOOT_HOLDS_ESA

//============================================================================






extern void *mac_flash_address;




// Private Data structures

#ifndef AT91_EMAC_RX_BUFF_SIZE
#define AT91_EMAC_RX_BUFF_SIZE  128
#endif

// Receive Buffer Descriptor
typedef struct rbd_s {
	cyg_uint32 addr;
	cyg_uint32 sr;
} rbd_t;

// Receive Buffer
typedef struct rb_s {
	cyg_uint8 rb[AT91_EMAC_RX_BUFF_SIZE];
} rb_t;

// Transmit Buffer Descriptor
typedef struct tbd_s {
	cyg_uint32 addr;
	cyg_uint32 sr;
} tbd_t;

// AT91 Ethernet private data
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
	eth_phy_access_t *phy;
	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;
	cyg_uint32 curr_tbd_idx;
	cyg_uint32 curr_rbd_idx;
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
	cyg_interrupt intr;
	cyg_handle_t intr_handle;
#endif
} at91_eth_priv_t;

extern void printf_user(const char *fmt, ...);
#define diag_printf printf_user

#define CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL 0

#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 0
#define debug1_printf(args...) diag_printf(args)
#else
#define debug1_printf(args...)
#endif
#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 1
#define debug2_printf(args...) diag_printf(args)
#else
#define debug2_printf(args...)
#endif

void print_buffs_read(void *p) {

	static at91_eth_priv_t *priv = NULL;

	if (p) {
		priv = (at91_eth_priv_t *) p;
	}
	if (!priv) {
		printf_user("print_buffs_read: priv == NULL\n");
		return;
	}

	int i;

	for (i = 0; i < CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; i++) {
		if (priv->rbd[i].addr & AT91_EMAC_RBD_ADDR_OWNER_SW) {
			printf_user("%2d used ", i);
		} else {
			printf_user("%2d      ", i);
		}
		printf_user("%08x\n", priv->rbd[i].sr);
	}
	printf_user("\n");
}

//============================================================================
// PHY access bits and pieces
// 

static void at91_mdio_enable(void) {
	cyg_uint32 val;
	HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
	val |= AT91_EMAC_NCR_MPE; /* enable management port */
	HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
}

static void at91_mdio_disable(void) {
	cyg_uint32 val;
	HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
	val &= ~AT91_EMAC_NCR_MPE; /* disable management port */
	HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCR, val);
}

// Write one of the PHY registers via the MII bus
static void at91_write_phy(int reg_addr, int phy_addr, unsigned short data) {
	cyg_uint32 val, cnt = 0;

	CYG_ASSERTC(reg_addr >= 0 && reg_addr <= AT91_EMAC_MAN_REGA_MASK);
	CYG_ASSERTC(phy_addr >= 0 && phy_addr <= AT91_EMAC_MAN_PHY_MASK);

	val = (AT91_EMAC_MAN_SOF |
	AT91_EMAC_MAN_WR |
	AT91_EMAC_MAN_CODE | AT91_EMAC_MAN_PHYA(phy_addr) | AT91_EMAC_MAN_REGA(reg_addr) | AT91_EMAC_MAN_DATA(data));

	HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_MAN, val);

	/* Wait until IDLE bit in Network Status register is set */
	while (cnt < 1000000) {
		HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val);
		if (!(val & AT91_EMAC_NSR_IDLE))
			break;
	}
	CYG_ASSERTC(cnt < 1000000);
}

// Read one of the PHY registers via the MII bus
static bool at91_read_phy(int reg_addr, int phy_addr, unsigned short *data) {
	cyg_uint32 val;

	CYG_ASSERTC(reg_addr >= 0 && reg_addr <= AT91_EMAC_MAN_REGA_MASK);
	CYG_ASSERTC(phy_addr >= 0 && phy_addr <= AT91_EMAC_MAN_PHY_MASK);

	val = (AT91_EMAC_MAN_SOF |
	AT91_EMAC_MAN_RD |
	AT91_EMAC_MAN_CODE | AT91_EMAC_MAN_PHYA(phy_addr) | AT91_EMAC_MAN_REGA(reg_addr));

	HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_MAN, val);
	/* Wait until IDLE bit in Network Status register is set */
	do {
		HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val);
	} while (!(val & AT91_EMAC_NSR_IDLE));

	HAL_DELAY_US(50);

	HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_MAN, val);
	*data = val & AT91_EMAC_MAN_DATA_MASK;

	return (true);
}

// Enable the MDIO bit in MAC control register so that we can talk to
// the PHY. Also set the clock divider so that MDC is less than 2.5MHz.
static void at91_init_phy(void) {
	cyg_uint32 cfg;
	cyg_uint32 div;

	HAL_READ_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg);
	cfg &= ~ AT91_EMAC_NCFG_CLK_MASK;

	div = (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / 2500000);
	if (div < 8) {
		cfg |= AT91_EMAC_NCFG_CLK_HCLK_8;
	} else if (div < 16) {
		cfg |= AT91_EMAC_NCFG_CLK_HCLK_16;
	} else if (div < 32) {
		cfg |= AT91_EMAC_NCFG_CLK_HCLK_32;
	} else if (div < 64) {
		cfg |= AT91_EMAC_NCFG_CLK_HCLK_64;
	} else {
		CYG_FAIL("Unable to program MII clock");
	}

	HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg);
}

ETH_PHY_REG_LEVEL_ACCESS_FUNS(at91_phy, at91_init_phy, NULL, at91_write_phy, at91_read_phy);

//======================================================================
// Receiver buffer handling

// Initialize the receiver buffers and descriptors
static void at91_rb_init(at91_eth_priv_t *priv) {
	int i;
	for (i = 0; i < CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; i++) {
		priv->rbd[i].addr = ((cyg_uint32) &priv->rb[i]) & AT91_EMAC_RBD_ADDR_MASK;
		priv->rbd[i].sr = 0;
	}
	// Set the wrap bit on the last entry
	priv->rbd[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS - 1].addr |=
	AT91_EMAC_RBD_ADDR_WRAP;
}

//======================================================================
// Transmit buffer handling

// Initialize the transmit buffer descriptors
static void 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;
	}
	// Set the wrap bit on the last entry
	priv->tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS - 1].sr |= AT91_EMAC_TBD_SR_WRAP;
}

//======================================================================
// Enable and Disable of the receiver and transmitter.

static void at91_disable_rx(at91_eth_priv_t *priv) {
	cyg_uint32 ctl;

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
	ctl &= ~AT91_EMAC_NCR_RE;
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void at91_disable_tx(at91_eth_priv_t *priv) {
	cyg_uint32 ctl;

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
	ctl &= ~AT91_EMAC_NCR_TX;
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void at91_enable_rx(at91_eth_priv_t *priv) {
	cyg_uint32 ctl;

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
	ctl |= AT91_EMAC_NCR_RE;
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void at91_enable_tx(at91_eth_priv_t *priv) {
	cyg_uint32 ctl;

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
	ctl |= AT91_EMAC_NCR_TX;
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

static void at91_enable(at91_eth_priv_t *priv) {
	at91_enable_tx(priv);
	at91_enable_rx(priv);
}

static void at91_disable(at91_eth_priv_t *priv) {
	at91_disable_tx(priv);
	at91_disable_rx(priv);
}

static void at91_start_transmitter(at91_eth_priv_t *priv) {
	cyg_uint32 ctl;

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
	ctl |= AT91_EMAC_NCR_TSTART;
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

//======================================================================
// Initialization code

// Configure the pins so that the EMAC has control of them. This
// assumes the MII is used, not the RMII
static void at91_cfg_pins(void) {
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EREFCK);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ECRS);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ECOL);

	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXDV);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX0);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX1);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX2);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERX3);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXER);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ERXCK);

	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETXEN);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX0);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX1);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX2);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETX3);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_ETXER);

	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDC);
	HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDIO);
}

// Set a specific address match to a given address. Packets received which
// match this address will be passed on.
static void at91_set_mac(at91_eth_priv_t * priv, cyg_uint8 * enaddr, int sa) {
	cyg_uint32 hi, lo;

	CYG_ASSERTC(sa > 0 && sa < 5);
	sa--;

	lo = ((enaddr[3] << 24) | (enaddr[2] << 16) | (enaddr[1] << 8) | (enaddr[0]));

	hi = ((enaddr[5] << 8) | (enaddr[4]));

	HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1L + (8*sa), lo);
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1H + (8*sa), hi);
}

static void at91_clear_stats(at91_eth_priv_t *priv) {
	cyg_uint32 ctl;

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, ctl);
	ctl |= AT91_EMAC_NCR_CSR;
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, ctl);
}

// Enable and Disable of the receiver and transmitter.
// Initialize the interface. This configures the interface ready for use.
// Interrupts are grabbed etc. This means the start function has
// little to do except enable the receiver
static bool at91_eth_init(struct cyg_netdevtab_entry *tab) {
	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;
#if 0
	unsigned char enaddr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR };
#else
	unsigned char default_enaddr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR };
	unsigned char *enaddr = mac_flash_address;
	if ((*enaddr) & 0xffffffffffff == 0xffffffffffff){
		enaddr = default_enaddr;
	}
#endif
	unsigned char enzero[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
	unsigned short phy_state = 0;
	cyg_uint32 ncfg = 0;

	debug1_printf("\nAT91_ETH: Initialising @ %x\n", priv->base);

	priv->tx_busy = false;
	priv->curr_tbd_idx = 0;
	priv->curr_rbd_idx = 0;

	// Enable the clock to the EMAC
	HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_EMAC);
	HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_PIOB);
	HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_PIOA);

	//at91_disable(priv);
	at91_cfg_pins();

	/* Enable  IO Clock */
	HAL_WRITE_UINT32(priv->base+AT91_EMAC_USRIO, AT91_EMAC_USRIO_CLKEN);

	/* Disable all the interrupts for the moment            */
	/* The Start function actually enables all that we need */
	//HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, 0x3FFF);
	// If we are building an interrupt enabled version, install the
	// interrupt handler
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
	debug1_printf("AT91_ETH: Installing Interrupts on IRQ %d\n", priv->intr_vector);
	cyg_drv_interrupt_create(priv->intr_vector, 4, (cyg_addrword_t) sc, at91_eth_isr, eth_drv_dsr,
			&priv->intr_handle, &priv->intr);

	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
	CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA(&enaddr[0], esa_ok);
	// If this call fails myMacAddr is unchanged and MAC address from
	// CDL is used
#endif

	if (!esa_ok) {
		// Can't figure out ESA
		debug1_printf("AT91_ETH - Warning! ESA unknown\n");
	}
	debug1_printf("AT91_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n", enaddr[0], enaddr[1], enaddr[2], enaddr[3],
			enaddr[4], enaddr[5]);

	// Give the EMAC its address
	at91_set_mac(priv, enaddr, 1);
	at91_set_mac(priv, enzero, 2);
	at91_set_mac(priv, enzero, 3);
	at91_set_mac(priv, enzero, 4);

	// Setup the receiver buffers and descriptors
	at91_rb_init(priv);

	// And tell the EMAC where the first receive buffer descriptor is
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_RBQP, (cyg_uint32 )priv->rbd);

	// 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);

	// Setup the PHY
	CYG_ASSERTC(priv->phy);

	at91_mdio_enable();
	if (!_eth_phy_init(priv->phy)) {
		at91_mdio_disable();
		return (false);
	}

	// Get the current mode and print it
	phy_state = _eth_phy_state(priv->phy);
	at91_mdio_disable();

	HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG, ncfg);

	if ((phy_state & ETH_PHY_STAT_LINK) != 0) {
		if (((phy_state & ETH_PHY_STAT_100MB) != 0)) {
			debug1_printf("AT91_ETH: 100Mbyte/s");
			ncfg |= AT91_EMAC_NCFG_SPD_100Mbps;
		} 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");
			ncfg |= AT91_EMAC_NCFG_FD;
		} else {
			debug1_printf(" Half Duplex\n");
			ncfg &= ~(AT91_EMAC_NCFG_FD);
		}
	} else {
		debug1_printf("AT91_ETH: No Link\n");
	}

	//Setup the network configuration
	ncfg |= (AT91_EMAC_NCFG_RLCE);

	HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG, ncfg);

	// Clear the Statistics counters;
	at91_clear_stats(priv);

	/* Clear the status registers */
	HAL_READ_UINT32(priv->base + AT91_EMAC_ISR, ncfg);
	HAL_READ_UINT32(priv->base + AT91_EMAC_RSR, ncfg);
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_RSR, ncfg);
	HAL_READ_UINT32(priv->base + AT91_EMAC_TSR, ncfg);
	HAL_WRITE_UINT32(priv->base + AT91_EMAC_TSR, ncfg);

	// Initialize the upper layer driver
	_eth_drv_init(sc, enaddr);

	return (true);
}

// 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;

	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;
	cyg_uint32 bits;

	print_buffs_read(priv);

	// 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);

	HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, bits);

	// Enable the receiver and transmitter
	at91_enable(priv);
}

// This function is called for low level "control" operations
static int at91_eth_control(struct eth_drv_sc *sc, unsigned long key, void *data, int length) {

	switch (key) {
	case ETH_DRV_SET_MAC_ADDRESS: {
		if (length >= ETHER_ADDR_LEN) {
			at91_eth_stop(sc);

			cyg_uint8 * enaddr = (cyg_uint8 *) data;
			debug1_printf("AT91_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n", enaddr[0], enaddr[1], enaddr[2],
					enaddr[3], enaddr[4], enaddr[5]);

			at91_set_mac((at91_eth_priv_t *) sc->driver_private, enaddr, 1);
			at91_eth_start(sc, enaddr, 0);
			return 0;
		}
		return 1;
	}
	default: {
		diag_printf("%s.%d: key %lx\n", __FUNCTION__, __LINE__, key);
		return (1);
	}
	}

}

// This function is called to see if another packet can be sent.
// It should return the number of packets which can be handled.
// Zero should be returned if the interface is busy and can not send
// any more.
//
// 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!
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) {
		can_send = 0;
	} else {
		can_send = 1;
	}
	return (can_send);
}

// This routine is called to send data to the hardware
static void 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;

	priv->last_tbd_idx = priv->curr_tbd_idx;

	for (i = 0; i < sg_len; i++) {
		priv->tbd[priv->curr_tbd_idx].addr = sg_list[i].buf;

		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;
		}

		if (priv->curr_tbd_idx < (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS - 1)) {
			priv->tbd[priv->curr_tbd_idx].sr = sr;
			priv->curr_tbd_idx++;
		} else {
			priv->tbd[priv->curr_tbd_idx].sr = (sr | AT91_EMAC_TBD_SR_WRAP);
			priv->curr_tbd_idx = 0;
		}
	}

	// 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) {
	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->last_tbd_idx = 0;
		} else {
			priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_USED;
			priv->last_tbd_idx++;
		}
	}
}

//======================================================================

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
static cyg_uint32 at91_eth_isr(cyg_vector_t vector, cyg_addrword_t data) {
	struct eth_drv_sc *sc = (struct eth_drv_sc *) data;
	at91_eth_priv_t *priv = (at91_eth_priv_t *) sc->driver_private;
	cyg_uint32 ret;
	cyg_uint32 isr;

	/* 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) {
		ret = CYG_ISR_CALL_DSR;
	}

	if (isr & AT91_EMAC_ISR_RCOM) {
		ret = CYG_ISR_CALL_DSR;
	}
	cyg_interrupt_acknowledge(vector);
	return (ret);
}
#endif


/* New code: old verision handles the 128 Byte buffer calculation not right and also can block under some circumstances */
static __attribute__((optimize("O3"))) void at91_eth_deliver(struct eth_drv_sc *sc) {
  at91_eth_priv_t *priv = (at91_eth_priv_t *) sc->driver_private;

  cyg_uint32 tsr;
  cyg_uint32 rsr;

  cyg_uint32 cnt;
  cyg_uint32 idx;

  /* Get the Transmit Status */
  HAL_READ_UINT32(priv->base+AT91_EMAC_TSR, tsr);
  HAL_WRITE_UINT32(priv->base+AT91_EMAC_TSR, tsr);

  /* Get the Receive Status */
  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 */
  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");
  }

  if (tsr & AT91_EMAC_TSR_BNQ)  //4
  {
    debug1_printf("AT91_ETH: Tx BEX\n");
  }

  if (tsr & AT91_EMAC_TSR_UND)  //6
  {
    debug1_printf("AT91_ETH: Tx UND\n");
  }

  /* Check that the last transmission is completed */
  if (tsr & AT91_EMAC_TSR_COMP) //5
  {
    at91_reset_tbd(priv);
    _eth_drv_tx_done(sc, priv->curr_tx_key, 0);
    priv->tx_busy = false;
  }

  if (rsr & AT91_EMAC_RSR_OVR) {
    debug1_printf("AT91_ETH: Rx OVR\n");
  }
  if (rsr & AT91_EMAC_RSR_BNA) {
    debug1_printf("AT91_ETH: Rx BNA\n");
  }

  /* Service the RX buffers when we get something */
  if (rsr & AT91_EMAC_RSR_REC) {

#define NOT_DEFINED -1

    cyg_int32 begpack = NOT_DEFINED;
    cyg_int32 i = 0;

    idx = priv->curr_rbd_idx;
    cnt = 0;

    for (i = 0; i < CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS; i++) {

      if (priv->rbd[idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW){

        if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_SOF){
          begpack = idx;
        }
        if (begpack != NOT_DEFINED){
          if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_LEN_MASK) {
            cnt += (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_LEN_MASK);
          } else {
            cnt += AT91_EMAC_RX_BUFF_SIZE;
          }
        } else {
          priv->rbd[idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
        }
        if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_EOF){
          if (begpack == NOT_DEFINED){
            cnt = 0;
            priv->rbd[idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
          } else {
            priv->curr_rbd_idx = begpack;
            _eth_drv_recv(sc, cnt);
          }
        }
      } else {
        if (begpack != NOT_DEFINED){
          while (begpack != idx){
            priv->rbd[begpack].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
            begpack++;
            if (begpack > CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS-1) {
              begpack = 0;
            }
          }

          begpack = NOT_DEFINED;
          cnt = 0;
        }
      }
      idx++;
      if (idx > CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS-1) {
        idx = 0;
      }
    }
  }

}

static __attribute__((optimize("O3"))) void at91_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) {

	at91_eth_priv_t *priv = (at91_eth_priv_t *) sc->driver_private;
	int i;
	cyg_uint32 bytes_in_buffer = 0;
	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;

	for (i = 0; i < sg_len; i++) {
		while (bytes_in_list < sg_list[i].len) {
			bytes_needed_list = sg_list[i].len - bytes_in_list;

			if (priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK) {
				bytes_in_buffer = ((priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK) - total_bytes)
						- buffer_pos;
			} else {
				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) {
				if (sg_buf != NULL)
					memcpy(&sg_buf[bytes_in_list], &priv->rb[priv->curr_rbd_idx].rb[buffer_pos],
							bytes_needed_list);
				bytes_in_list += bytes_needed_list;
				buffer_pos += bytes_needed_list;
				total_bytes += bytes_needed_list;
			} else {
				if (sg_buf != NULL)
					memcpy(&sg_buf[bytes_in_list], &priv->rb[priv->curr_rbd_idx].rb[buffer_pos],
							bytes_in_buffer);
				bytes_in_list += bytes_in_buffer;
				total_bytes += bytes_in_buffer;

				/* Step our buffer on one */
				priv->rbd[priv->curr_rbd_idx].addr &= ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
				priv->curr_rbd_idx++;
				if (priv->curr_rbd_idx > CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS-1) {
					priv->curr_rbd_idx = 0;
				}
				buffer_pos = 0;
			}
		}
	}
}



// routine called to handle ethernet controller in polled mode
static void at91_eth_poll(struct eth_drv_sc *sc) {
	/* Service the buffers */
	at91_eth_deliver(sc);
}

static int at91_eth_int_vector(struct eth_drv_sc *sc) {
	return (CYGNUM_HAL_INTERRUPT_EMAC);
}

at91_eth_priv_t at91_priv_data =
		{ .intr_vector = CYGNUM_HAL_INTERRUPT_EMAC, .base = AT91_EMAC, .phy = &at91_phy };

ETH_DRV_SC(at91_sc,
		&at91_priv_data,       // Driver specific data
		"eth0",// Name for this interface
		at91_eth_start, at91_eth_stop, at91_eth_control, at91_eth_can_send, at91_eth_send, at91_eth_recv,
		at91_eth_deliver, at91_eth_poll, at91_eth_int_vector);

NETDEVTAB_ENTRY(at91_netdev, "at91", at91_eth_init, &at91_sc);

// EOF if_at91.c

[-- Attachment #4: Type: text/plain, Size: 148 bytes --]

-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2015-06-10 12:12 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <a5143a33e2b4952478f8291234b9b4b1@haven.inspiration.ch>
2015-06-10 12:12 ` [ECOS] Re: at91 emac ethernet driver lwip diff patch Lambrecht Jürgen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).