public inbox for ecos-discuss@sourceware.org
 help / color / mirror / Atom feed
* [ECOS] Scheduler Lock without Kernel
@ 2011-03-01 12:26 Martin Laabs
  2011-03-01 16:42 ` Daniel Helgason
  0 siblings, 1 reply; 2+ messages in thread
From: Martin Laabs @ 2011-03-01 12:26 UTC (permalink / raw)
  To: eCos Discuss

Hi,

I currently improve the Ethernet driver for the at91sam9 family of CPUs. 
However - Since this CPU has a cache and is using DMA for data exchange 
with the MAC I have to lock the scheduler while manipulating these memory 
structures. (This is to ensure, that the cache is coherent while doing this 
manipulations)
Since the code should run with the kernel as well without it I'd like to 
know whether it's save to call the cyg_scheduler_lock without being in 
kernel mode.
But I fear I have to write a macro that distinguish between "with kernel" 
and "hal only". Am I right? Which symbol is best to distinguish between 
thees two possibilities?

Thank you,
  Martin laabs


-- 
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] 2+ messages in thread

* Re: [ECOS] Scheduler Lock without Kernel
  2011-03-01 12:26 [ECOS] Scheduler Lock without Kernel Martin Laabs
@ 2011-03-01 16:42 ` Daniel Helgason
  0 siblings, 0 replies; 2+ messages in thread
From: Daniel Helgason @ 2011-03-01 16:42 UTC (permalink / raw)
  To: eCos Discuss

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

On Tue, 2011-03-01 at 13:25 +0100, Martin Laabs wrote:
> Hi,
> 
> I currently improve the Ethernet driver for the at91sam9 family of CPUs. 
> However - Since this CPU has a cache and is using DMA for data exchange 
> with the MAC I have to lock the scheduler while manipulating these memory 
> structures. (This is to ensure, that the cache is coherent while doing this 
> manipulations)
> Since the code should run with the kernel as well without it I'd like to 
> know whether it's save to call the cyg_scheduler_lock without being in 
> kernel mode.
> But I fear I have to write a macro that distinguish between "with kernel" 
> and "hal only". Am I right? Which symbol is best to distinguish between 
> thees two possibilities?
> ...

Martin!

Attached is my work-in-progress on a AT91 ethernet driver. It works well
with the AT91SAM9 devices, both with a eCos kernel and with Redboot. It
needs the MMU to create a shadow of RAM that is uncached. Anyway, there
might be something useful here for you. There are some private changes
marked "Exegin" that you can remove.

-- 
+------------------------------------
| Daniel Helgason <dhelgason@shaw.ca>


[-- Attachment #2: if_at91.c --]
[-- Type: text/x-csrc, Size: 40185 bytes --]

//==========================================================================
//
//      if_at91.c
//
//
//
//==========================================================================
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Daniel Helgason
// Contributors: Andrew Lunn, John Eigelaar
// Date:         2010-01-01
// 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/hal_cache.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>

#ifndef MIN
#define MIN(x,y) ((x)<(y) ? (x) : (y))
#endif

// Cache translation
#ifndef CYGARC_UNCACHED_ADDRESS
#define CYGARC_UNCACHED_ADDRESS(x) (x)
#endif

#define AT91_ETH_RX_INT_MASK \
          (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_RBNA | AT91_EMAC_ISR_ROVR)

/*
#define AT91_ETH_TX_INT_MASK \
          (AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND | AT91_EMAC_ISR_RTRY | \
           AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM)
*/
#define AT91_ETH_TX_INT_MASK \
          (AT91_EMAC_ISR_TUND | AT91_EMAC_ISR_RTRY | \
           AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM)

#define AT91_ETH_INT_MASK (AT91_ETH_RX_INT_MASK | AT91_ETH_TX_INT_MASK)

#define AT91_ETH_NEXT_RBD(idx);                         \
  CYG_MACRO_START                                       \
    idx++;                                              \
    if (idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)        \
      idx = 0;                                          \
  CYG_MACRO_END

#define AT91_ETH_NEXT_TBD(idx);                         \
  CYG_MACRO_START                                       \
    idx++;                                              \
    if (idx >= CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS)        \
      idx = 0;                                          \
  CYG_MACRO_END

// Set up the level of debug output
#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 0
#define AT91_ETH_DEBUG1(cond, args...)                  \
  CYG_MACRO_START                                       \
    if ((cond))                                         \
      diag_printf(args);                                \
  CYG_MACRO_END
#else
#define AT91_ETH_DEBUG1(args...) CYG_EMPTY_STATEMENT
#endif

#if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 1
#define AT91_ETH_DEBUG2(cond, args...)                  \
  CYG_MACRO_START                                       \
    if ((cond))                                         \
      diag_printf(args);                                \
  CYG_MACRO_END
#else
#define AT91_ETH_DEBUG2(args...) CYG_EMPTY_STATEMENT
#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

// BUGBUG dkh - Need better MAC address management.

// 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
  static unsigned char at91_eth0_mac_addr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR };

  RedBoot_config_option("Network hardware address [MAC] for eth0",
                        eth0_esa_data,
                        ALWAYS_ENABLED, true,
                        CONFIG_ESA, at91_eth0_mac_addr);
  #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

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

// Private Data structures

#ifndef AT91_EMAC_RX_BUFF_SIZE
#define AT91_EMAC_RX_BUFF_SIZE  128
#endif

// Receive Buffer Descriptor
typedef struct rbd_s
{
  volatile cyg_uint32 addr;
  volatile 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;
  volatile cyg_uint32 sr;
} tbd_t;

// AT91 Ethernet private data
typedef struct at91_eth_priv_s
{
  cyg_uint32 int_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] __attribute__ ((aligned (8)));
  rb_t  rb[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS] __attribute__ ((aligned (8)));
  tbd_t tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS] __attribute__ ((aligned (8)));
  unsigned long tx_keys[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS];
  cyg_uint32 isr;
  cyg_uint32 tx_head_idx;
  cyg_uint32 tx_tail_idx;
  cyg_uint32 tx_free;
  cyg_uint32 tx_max_frame_sglen;
  cyg_bool tx_busy;
  cyg_uint32 rx_head_idx;
  cyg_uint32 rx_tail_idx;
  cyg_uint32 rx_frame_cnt;
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
  cyg_interrupt intr;
  cyg_handle_t  intr_handle;
#endif
} at91_eth_priv_t;

// AT91 multicast hash mask (64-bit hash in two 32-bit words)
typedef struct mc_hash_mask_s
{
  cyg_uint32 hi;
  cyg_uint32 lo;
} at91_mc_hash_mask_t;

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

// Private Data

// Hash masks for multicast MAC addresses on AT91 EMAC.
static at91_mc_hash_mask_t at91_mc_hash_masks[] =
{
//  high word   low word
  { 0x00000410, 0x41041041 },
  { 0x00000820, 0x82082082 },
  { 0x00001041, 0x04104104 },
  { 0x00002082, 0x08208208 },
  { 0x00004104, 0x10410410 },
  { 0x00008208, 0x20820820 }
};

// Exegin specific. Registered callbacks.
static void    (*at91_eth_rx_callback_func)(void) = NULL;
static void    (*at91_eth_tx_callback_func)(void) = NULL;


//============================================================================
// Exegin specific. Register callbacks.
//

void
at91_eth_register_rx_callback(void (*rx_callback_func)(void))
{
  at91_eth_rx_callback_func = rx_callback_func;
} /* at91_eth_register_rx_callback */

void
at91_eth_register_tx_callback(void (*tx_callback_func)(void))
{
  at91_eth_tx_callback_func = tx_callback_func;
} /* at91_eth_register_tx_callback */

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

  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 for operation to complete
  do {
    HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val);
  } while(!(val & AT91_EMAC_NSR_IDLE));
}

// 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 for operation to complete
  do {
    HAL_READ_UINT32((AT91_EMAC + AT91_EMAC_NSR), val);
  } while(!(val & AT91_EMAC_NSR_IDLE));

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

  return (true);
}

/* Exegin specific. */
/* Give public names to the phy r/w functions for the Q5x firmware. */
void
phy_write(int reg_addr, int phy_addr, unsigned short data)
{
  at91_write_phy(reg_addr, phy_addr, data);
}

bool
phy_read(int reg_addr, int phy_addr, unsigned short *data)
{
  return at91_read_phy(reg_addr, phy_addr, data);
}

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


//======================================================================
// Transmit and Receive buffer-descriptor initialization.

// Initialize the receiver buffer 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;

  HAL_DCACHE_STORE(priv->rbd, sizeof(priv->rbd));
}

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

  HAL_DCACHE_STORE(priv->tbd, sizeof(priv->tbd));
}


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

  // Stop transmitter.
  HAL_READ_UINT32(priv->base + AT91_EMAC_NCR, reg_val);
  reg_val &= ~AT91_EMAC_NCR_TX;
  reg_val |= AT91_EMAC_NCR_THALT;
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCR, reg_val);

  // Wait for transmitter to stop.
  do {
    HAL_READ_UINT32(priv->base + AT91_EMAC_TSR, reg_val);
  } while (reg_val & AT91_EMAC_TSR_TXIDLE);
}

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);
  if (!(ctl & AT91_EMAC_NCR_TX)) {
    priv->tx_free = CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS - 1;
    priv->tx_head_idx = priv->tx_tail_idx = 0;
    at91_tb_init(priv);
    ctl |= AT91_EMAC_NCR_TX;
    ctl &= ~AT91_EMAC_NCR_THALT;
    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.
static void
at91_cfg_pins(void)
{
#ifdef CYGHWR_DEV_ETH_ARM_AT91_USE_RMII
  // RMII doesn't need all the EMAC IO pins
  HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EREFCK);

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

  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_EMDC);
  HAL_ARM_AT91_PIO_CFG(AT91_EMAC_EMDIO);
#else
  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);
#endif
}

// Set a specific-address match register to a given MAC address.
// Packets received which match this address will be passed on.
static void
at91_set_mac(at91_eth_priv_t *priv, cyg_uint8 *mac_addr, int sa)
{
  cyg_uint32 mac_hi, mac_lo;

  // Adjust specific-address register index.
  CYG_ASSERTC(sa > 0 && sa < 5);
  sa--;

  // Split MAC address into two words.
  mac_lo = (mac_addr[3] << 24) |
           (mac_addr[2] << 16) |
           (mac_addr[1] <<  8) |
            mac_addr[0];

  mac_hi = (mac_addr[5] <<  8) |
            mac_addr[4];

  // Write MAC address to the specific-address register.
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1L + (8 * sa), mac_lo);
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_SA1H + (8 * sa), mac_hi);
}

// Compute number of bits in word (aka population count).
static int
at91_mc_bitcnt(cyg_uint32 bits)
{
  bits -= (bits >> 1) & 0x55555555;
  bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333);
  bits = ((bits >> 4) + bits) & 0x0f0f0f0f;
  bits += bits >> 8;
  bits += bits >> 16;

  return bits & 0x3f;
}

// Add a multicast MAC address to the hardware and enable hash matching.
// Packets received which match these addresses will be passed on.
static void
at91_add_mc_mac(at91_eth_priv_t * priv, cyg_uint8 * mc_addr)
{
  cyg_uint32 t, mc_lo, mc_hi, hash_lo, hash_hi;
  int i, bit_idx, bit_cnt;

  // Split multicast mac address into two words.
  mc_lo = (mc_addr[3] << 24) |
          (mc_addr[2] << 16) |
          (mc_addr[1] <<  8) |
           mc_addr[0];

  mc_hi = (mc_addr[5] << 8) |
           mc_addr[4];

  // Build the 6-bit hash_bit index.
  bit_idx = 0;
  for (i = 0; i < 6; i++) {
    bit_cnt =  at91_mc_bitcnt(mc_lo & at91_mc_hash_masks[i].lo);
    bit_cnt += at91_mc_bitcnt(mc_hi & at91_mc_hash_masks[i].hi);

    bit_idx |= (bit_cnt & 1) << i;
  }

  // Read the current 64-bit hardware hash value.
  HAL_READ_UINT32(priv->base + AT91_EMAC_HRB, hash_lo);
  HAL_READ_UINT32(priv->base + AT91_EMAC_HRT, hash_hi);

  // Set a single bit in the hash value according to the index.
  if (bit_idx > 31)
    hash_hi |= 1 << (bit_idx - 32);
  else
    hash_lo |= 1 << bit_idx;

  // Write the new 64-bit hardware hash value.
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_HRB, hash_lo);
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_HRT, hash_hi);

  // Enable hardware to do multicast hash matching.
  HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG, t);
  t |= AT91_EMAC_NCFG_MTI;
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG, t);
}

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

// Initialize and configure the interface 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;
  unsigned char mac_addr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR };
  unsigned char mac_addr_none[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
  unsigned short phy_state = 0;
  cyg_uint32 ncfg = 0;
#ifdef CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA
  bool esa_ok = false;
#endif

  AT91_ETH_DEBUG1(true, "AT91_ETH: Initialising at 0x%08x\n", priv->base);

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

  at91_disable(priv);

  priv->tx_free = CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS - 1;
  priv->tx_head_idx = priv->tx_tail_idx = 0;
  priv->tx_max_frame_sglen = 3;
  priv->rx_head_idx = priv->rx_tail_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_cfg_pins();

  /* Enable IO clock and either MII or RMII interface */
#ifdef CYGHWR_DEV_ETH_ARM_AT91_USE_RMII
  HAL_WRITE_UINT32(priv->base+AT91_EMAC_USRIO,
                   AT91_EMAC_USRIO_CLKEN | AT91_EMAC_USRIO_RMII);
#else
  HAL_WRITE_UINT32(priv->base+AT91_EMAC_USRIO, AT91_EMAC_USRIO_CLKEN);
#endif

  // If we are building an interrupt enabled version,
  // then install the interrupt handler
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
  AT91_ETH_DEBUG1(true, "AT91_ETH: Installing Interrupts on IRQ %d\n",
                  priv->int_vector);
  cyg_drv_interrupt_create(priv->int_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->int_vector);
#endif

#ifdef CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA
  // Get MAC address from RedBoot configuration variables.
  CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA(&mac_addr[0], esa_ok);
  // If that fails, then MAC address is unchanged and the
  // MAC address from CDL is used.
    AT91_ETH_DEBUG1(esa_ok, "AT91_ETH: Warning! ESA unknown\n");
#endif

  AT91_ETH_DEBUG1(true, "AT91_ETH: MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
                  mac_addr[0], mac_addr[1], mac_addr[2],
                  mac_addr[3], mac_addr[4], mac_addr[5]);

  // Give the EMAC its MAC address
  at91_set_mac(priv, mac_addr, 1);
  at91_set_mac(priv, mac_addr_none, 2);
  at91_set_mac(priv, mac_addr_none, 3);
  at91_set_mac(priv, mac_addr_none, 4);

  // Setup the receiver buffer descriptors and tell EMAC where they are
  at91_rb_init(priv);
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_RBQP, (cyg_uint32)priv->rbd);

  // Setup the transmit buffer descriptors and tell EMAC where they are
  at91_tb_init(priv);
  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);
  }

  // Set EMAC network configuration to match PHY state
  HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG, ncfg);

  phy_state = _eth_phy_state(priv->phy);
//    at91_mdio_disable();
  AT91_ETH_DEBUG1(!(phy_state & ETH_PHY_STAT_LINK), "AT91_ETH: No Link\n");
  if ((phy_state & ETH_PHY_STAT_LINK) != 0) {
    if (((phy_state & ETH_PHY_STAT_100MB) != 0)) {
      AT91_ETH_DEBUG1(true, "AT91_ETH: 100Mbs");
      ncfg |= AT91_EMAC_NCFG_SPD_100Mbps;
    }
    else {
      AT91_ETH_DEBUG1(true, "AT91_ETH: 10Mbps");
      ncfg &= ~(AT91_EMAC_NCFG_SPD_100Mbps);
    }

    if ((phy_state & ETH_PHY_STAT_FDX)) {
      AT91_ETH_DEBUG1(true, " Full Duplex\n");
      ncfg |= AT91_EMAC_NCFG_FD;
    }
    else {
      AT91_ETH_DEBUG1(true, " Half Duplex\n");
      ncfg &= ~(AT91_EMAC_NCFG_FD);
    }
  }

  // Write the network-configuration register
  ncfg |= (AT91_EMAC_NCFG_RLCE);
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG, ncfg);

  // Clear the statistics counters
  at91_clear_stats(priv);

  /* Clear the EMAC 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, mac_addr);

  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
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, AT91_ETH_INT_MASK);
#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 *mac_addr, int flags)
{
  at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;

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

//#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, AT91_ETH_INT_MASK);
//#endif
}

// 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) {
    // Set Ethernet MAC address
    case ETH_DRV_SET_MAC_ADDRESS:
    {
      cyg_uint8 *mac_addr = (cyg_uint8 *)data;

      if (mac_addr == NULL || length < ETHER_ADDR_LEN)
        return 1;

      AT91_ETH_DEBUG1(true, "AT91_ETH: set MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
                      mac_addr[0], mac_addr[1], mac_addr[2],
                      mac_addr[3], mac_addr[4], mac_addr[5]);

      at91_eth_stop(sc);
      at91_set_mac(priv, mac_addr, 1);
      at91_eth_start(sc, mac_addr, 0);
      return 0;
    }

#ifdef ETH_DRV_GET_MAC_ADDRESS
    // Get Ethernet MAC address from hardware
    case ETH_DRV_GET_MAC_ADDRESS:
    {
      cyg_uint32 mac_hi, mac_lo;
      cyg_uint8 *mac_addr = (cyg_uint8 *)data;

      if (mac_addr == NULL || length < ETHER_ADDR_LEN)
        return 1;

      HAL_READ_UINT32(priv->base + AT91_EMAC_SA1L, mac_lo);
      HAL_READ_UINT32(priv->base + AT91_EMAC_SA1H, mac_hi);

      mac_addr[0] =         mac_lo & 0xFF;
      mac_addr[1] = (mac_lo >>  8) & 0xFF;
      mac_addr[2] = (mac_lo >> 16) & 0xFF;
      mac_addr[3] = (mac_lo >> 24) & 0xFF;
      mac_addr[4] =         mac_hi & 0xFF;
      mac_addr[5] = (mac_hi >>  8) & 0xFF;

      return 0;
    }
#endif

    // Set specified multicast MAC addresses or clear all
    case ETH_DRV_SET_MC_LIST:
    {
      int mc_cnt;
      cyg_uint32 t;
      struct eth_drv_mc_list *mc_list = (struct eth_drv_mc_list *)data;

      if (mc_list == NULL || length != sizeof(struct eth_drv_mc_list))
        return 1;

      mc_cnt = mc_list->len;
      if (mc_cnt > 0) {
        // Enable recognition of each multicast MAC address in list.
        while (mc_cnt) {
          at91_add_mc_mac(priv, &mc_list->addrs[--mc_cnt][0]);
          AT91_ETH_DEBUG1(true, "AT91_ETH: add multicast MAC "
                                "%02x:%02x:%02x:%02x:%02x:%02x\n",
                          mc_list->addrs[mc_cnt][0],
                          mc_list->addrs[mc_cnt][1],
                          mc_list->addrs[mc_cnt][2],
                          mc_list->addrs[mc_cnt][3],
                          mc_list->addrs[mc_cnt][4],
                          mc_list->addrs[mc_cnt][5]);
        }
      }
      else {
        // Disable recognition of all multicast addresses.
        AT91_ETH_DEBUG1(true, "AT91_ETH: disable all multicast MACs\n");
        HAL_WRITE_UINT32(priv->base + AT91_EMAC_HRB, 0);
        HAL_WRITE_UINT32(priv->base + AT91_EMAC_HRT, 0);
        HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG, t);
        t &= ~AT91_EMAC_NCFG_MTI;
        HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG, t);
      }

      return 0;
    }

    // Enable all multicast MAC addresses
    case ETH_DRV_SET_MC_ALL:
    {
      cyg_uint32 t;

      AT91_ETH_DEBUG1(true, "AT91_ETH: enable all multicast MACs\n");
      HAL_WRITE_UINT32(priv->base + AT91_EMAC_HRB, 0xFFFFFFFF);
      HAL_WRITE_UINT32(priv->base + AT91_EMAC_HRT, 0xFFFFFFFF);
      HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG, t);
      t |= AT91_EMAC_NCFG_MTI;
      HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG, t);

      return 0;
    }

    // Unknown or unhandled key
    default:
    {
      AT91_ETH_DEBUG1(true, "AT91_ETH: unknown IOCTL key %lx\n", key);
      return 1;
    }
  } // switch
}

// 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 start
// with the assumuption that a typical packet will not have more than
// three such entries, but we keep track of the largest number of entries
// ever used and compute the return value based on that.
static int
at91_eth_can_send(struct eth_drv_sc *sc)
{
  at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;

  if (priv->tx_busy)
    return 0;
  else
    return priv->tx_free / priv->tx_max_frame_sglen;
}


// 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, head_idx;
  tbd_t *tbd;

  CYG_ASSERT(!priv->tx_busy, "send called when busy\n");

  // BUGBUG dkh - perhaps this needs to be a real check, not an assert
  CYG_ASSERT(priv->tx_free >= sg_len, "not enough free tx descriptors\n");
//  if (sg_len > priv->tx_free) {
//    (sc->funs->eth_drv->tx_done)(sc, key, 1);
//    return;
//  }

  // Keep track of the largest number of scatter/gather entries
  // ever used by a transmitted packet.
  if (sg_len > priv->tx_max_frame_sglen) {
    priv->tx_max_frame_sglen = sg_len;
    AT91_ETH_DEBUG1(true, "AT91_ETH: Tx sg_len increased to %d\n", sg_len);
  }

  // Disable transmit interrupts.
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, AT91_ETH_TX_INT_MASK);

  head_idx = priv->tx_head_idx;
  for(i = 0; i < sg_len; i++) {
    HAL_DCACHE_STORE(sg_list[i].buf, sg_list[i].len);
    HAL_MEMORY_BARRIER();
    sr = sg_list[i].len & AT91_EMAC_TBD_SR_LEN_MASK;

    // If this is the first buffer in the frame, then keep USED bit on for now.
    if (i == 0)
      sr |= AT91_EMAC_TBD_SR_USED;

    // If this is the last buffer in the frame, then set EOF bit.
    // Also save the eCos key so we can return status to eCos at tx completion.
    if (i == (sg_len - 1)) {
      sr |= AT91_EMAC_TBD_SR_EOF;
      priv->tx_keys[head_idx] = key;
    }

    // If this is the last buffer in the ring, then set the WRAP bit.
    if (head_idx == (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS - 1))
      sr |= AT91_EMAC_TBD_SR_WRAP;

    // Write buffer address and status to EMAC descriptor.
    tbd = (tbd_t *)CYGARC_UNCACHED_ADDRESS(&priv->tbd[head_idx]);
    tbd->addr = sg_list[i].buf;
    tbd->sr = sr;
    AT91_ETH_NEXT_TBD(head_idx);
  }

  // Release the frame to the EMAC
  cyg_drv_dsr_lock();
  priv->tx_free -= sg_len;
  tbd = (tbd_t *)CYGARC_UNCACHED_ADDRESS(&priv->tbd[priv->tx_head_idx]);
  tbd->sr &= ~AT91_EMAC_TBD_SR_USED;
  priv->tx_head_idx = head_idx;
  cyg_drv_dsr_unlock();

  // Enable transmit interrupts and start transmit.
  HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, AT91_ETH_TX_INT_MASK);
  at91_start_transmitter(priv);
}


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

#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_interrupt_mask(priv->int_vector);
  cyg_interrupt_acknowledge(priv->int_vector);
  return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
}
#endif


/* Handle transmit-complete events. */
static void
at91_eth_tx(struct eth_drv_sc *sc)
{
  at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
  cyg_uint32 tsr, sr;
  cyg_uint32 processed_buffers;
  cyg_bool tx_err, is_sof;
  tbd_t *tbd;

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

  AT91_ETH_DEBUG1(true, "AT91_ETH: TxStat Reg = 0x%08x\n", tsr);
  AT91_ETH_DEBUG1(tsr & AT91_EMAC_TSR_OVR, "Tx_UBR ");
  AT91_ETH_DEBUG1(tsr & AT91_EMAC_TSR_COL, "Tx_COL ");
  AT91_ETH_DEBUG2((tsr & AT91_EMAC_TSR_TXIDLE) == 0, "Tx_IDLE ");
  AT91_ETH_DEBUG1(tsr & AT91_EMAC_TSR_RLE, "Tx_RLE ");
  AT91_ETH_DEBUG1(tsr & AT91_EMAC_TSR_BNQ, "Tx_BEX ");
  AT91_ETH_DEBUG1(tsr & AT91_EMAC_TSR_UND, "Tx_UND ");
  AT91_ETH_DEBUG1(tsr & AT91_EMAC_TSR_COMP, "Tx_COMP ");
  AT91_ETH_DEBUG1(true, "\n");

  // Only do work if the transmit-status register indicates work to do.
  if (tsr & (AT91_EMAC_TSR_COMP | AT91_EMAC_TSR_RLE |
             AT91_EMAC_TSR_BNQ | AT91_EMAC_TSR_UND)) {

    if (tsr & (AT91_EMAC_TSR_RLE | AT91_EMAC_TSR_BNQ | AT91_EMAC_TSR_UND))
      tx_err = true;
    else
      tx_err = false;

    priv->tx_busy = true;
    processed_buffers = 0;
    is_sof = true;

    /* For each possible outstanding descriptor. */
    while (priv->tx_tail_idx != priv->tx_head_idx) {
      tbd = (tbd_t *)CYGARC_UNCACHED_ADDRESS(&priv->tbd[priv->tx_tail_idx]);
      sr = tbd->sr;
      AT91_ETH_DEBUG2(true, "AT91_ETH: TX buffer status at %d = 0x%08x\n",
                      priv->tx_tail_idx, sr);

      // If this descriptor is the first in a frame, then check that
      // the EMAC has processed it. If so, record any errors.
      if (is_sof) {
        // If no previous errors were indicated and the EMAC hasn't
        // processed this descriptor yet, then we are caught up with it.
        // Don't look at any remaining descriptors until the EMAC
        // indicates completion.
        if (!tx_err && !(sr & AT91_EMAC_TBD_SR_USED))
          break;

        // Examine the error bits
        if (sr & (AT91_EMAC_TBD_SR_EXHAUST |
                  AT91_EMAC_TBD_SR_TXUNDER | AT91_EMAC_TBD_SR_RTRY)) {
          tx_err = true;
          AT91_ETH_DEBUG1(tx_err, "AT91_ETH: TX frame at %d has error\n",
                          priv->tx_tail_idx);
        }

        // Next buffer, if any, is not SOF
        is_sof = false;
      }

      // If this descriptor is the last in a frame, then notify
      // the TCP/IP stack about the status of the transmit.
      if (sr & AT91_EMAC_TBD_SR_EOF) {
        _eth_drv_tx_done(sc, priv->tx_keys[priv->tx_tail_idx], tx_err);

        // Exegin specific. Do transmit callback.
        if (!tx_err && at91_eth_tx_callback_func)
          (at91_eth_tx_callback_func)();

        // Next buffer, if any, is SOF.
        is_sof = true;
      }

      // Mark descriptor as used, preserving wrap-bit.
      sr &= AT91_EMAC_TBD_SR_WRAP;
      sr |= AT91_EMAC_TBD_SR_USED;
      tbd->sr = sr;
      processed_buffers++;
      AT91_ETH_NEXT_TBD(priv->tx_tail_idx);
    } /* while */

    cyg_drv_dsr_lock();
    priv->tx_free += processed_buffers;

    // If error occured that caused EMAC to reset the TX hardware, then
    // we need to follow the TX hardware pointer.
    if (tx_err) {
      AT91_ETH_DEBUG1(true, "AT91_ETH: TX error - reset TX buffer indexes\n");
      at91_disable_tx(priv);
      at91_enable_tx(priv);
      at91_start_transmitter(priv);
    }
    cyg_drv_dsr_unlock();

    priv->tx_busy = false;
  } // endif transmit-status register indicates work to do
}


/* Return RX descriptors to EMAC, up to but not including, current head */
static void at91_return_rx_bds(at91_eth_priv_t *priv, int rbd_idx)
{
  rbd_t *rbd;

  while (rbd_idx != priv->rx_head_idx) {
    rbd = (rbd_t *)CYGARC_UNCACHED_ADDRESS(&priv->rbd[rbd_idx]);
    rbd->addr &= ~AT91_EMAC_RBD_ADDR_OWNER_SW;
    /* WRITE MEMORY BARRIER */
    HAL_MEMORY_BARRIER();
    AT91_ETH_NEXT_RBD(rbd_idx);
  }
}


/* Handle receive-ready events. */
static void
at91_eth_rx(struct eth_drv_sc *sc)
{
  at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
  cyg_int32 sof_idx = -1;
  cyg_uint32 rsr, sr;
  rbd_t *rbd;

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

  AT91_ETH_DEBUG1(true, "AT91_ETH: RxStat Reg = 0x%08x\n", rsr);
  AT91_ETH_DEBUG1(rsr & AT91_EMAC_RSR_BNA, "Rx_BNA ");
  AT91_ETH_DEBUG1(rsr & AT91_EMAC_RSR_OVR, "Rx_OVR ");
  AT91_ETH_DEBUG1(rsr & AT91_EMAC_RSR_REC, "Rx_REC ");
  AT91_ETH_DEBUG1(true, "\n");

  /* Service the RX buffers if required */
  if (rsr & AT91_EMAC_RSR_REC) {
    rbd = (rbd_t *)CYGARC_UNCACHED_ADDRESS(&priv->rbd[priv->rx_head_idx]);
    // For all descriptors that the EMAC has given ownership to us.
    while (rbd->addr & AT91_EMAC_RBD_ADDR_OWNER_SW) {
      /* READ MEMORY BARRIER */
      /* HAL_MEMORY_BARRIER(); */
      sr = rbd->sr;

      if (sr & AT91_EMAC_RBD_SR_SOF) {
        if (sof_idx != -1) {
          AT91_ETH_DEBUG2(true, "AT91_ETH: RX discarding fragments\n");
          at91_return_rx_bds(priv, sof_idx);
        }
        sof_idx = priv->rx_head_idx;
      }

      // Advance head ptr here, not at end of while_loop.
      AT91_ETH_NEXT_RBD(priv->rx_head_idx);
      rbd = (rbd_t *)CYGARC_UNCACHED_ADDRESS(&priv->rbd[priv->rx_head_idx]);

      if (sr & AT91_EMAC_RBD_SR_EOF) {
        // This is assumed to be unrecoverable hardware failure.
        CYG_ASSERT(sof_idx != -1, "receiver found EOF but no SOF\n");

        // Exegin specific. Do receive callback.
        if (at91_eth_rx_callback_func)
          (at91_eth_rx_callback_func)();

        priv->rx_tail_idx = sof_idx;
        priv->rx_frame_cnt = sr & AT91_EMAC_RBD_SR_LEN_MASK;
        _eth_drv_recv(sc, priv->rx_frame_cnt);
        at91_return_rx_bds(priv, sof_idx);
        sof_idx = -1;
      }
    }

    // If we found SOF but didn't find matching EOF.
    if (sof_idx != -1) {
      AT91_ETH_DEBUG2(true, "AT91_ETH: RX discarding fragments\n");
      priv->rx_head_idx = sof_idx;
    }
  }
}


/* Called by the TCP/IP stack when it wants us to handle hardware events. */
static void
at91_eth_deliver(struct eth_drv_sc *sc)
{
  at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
  cyg_uint32 isr, imr;

  do {
    // Get interrupt status. This clears the register.
    HAL_READ_UINT32(priv->base + AT91_EMAC_ISR, isr);
    AT91_ETH_DEBUG2(true, "AT91_ETH: IrqStat Reg = 0x%08x\n", isr);

    // This is assumed to be unrecoverable hardware failure.
    // Unless caused by slow clock?
    CYG_ASSERT(!(isr & AT91_EMAC_ISR_HRESP), "EMAC DMA/bus failed\n");

    // Get interrupt mask.
    HAL_READ_UINT32(priv->base + AT91_EMAC_IMR, imr);

    isr = isr & ~imr & AT91_ETH_INT_MASK;

    if (isr & AT91_ETH_RX_INT_MASK)
      at91_eth_rx(sc);

    if (isr & AT91_ETH_TX_INT_MASK)
      at91_eth_tx(sc);
  } while (isr);

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
  cyg_interrupt_unmask(priv->int_vector);
#endif
}


/* Called by the TCP/IP stack when it wants us to unload receive buffers. */
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;
  cyg_uint32 rb_len, rb_offset;
  cyg_uint32 sg_idx, sg_offset;
  cyg_uint32 copy_cnt;

  // If the TCP/IP stack is broken or out of buffers.
  CYG_ASSERT(sg_list != NULL, "TCP/IP stack error or out of buffers\n");

  // Init working vars
  sg_idx = sg_offset = rb_offset = 0;

  // Get count of bytes in current buffer.
  rb_len = MIN(priv->rx_frame_cnt, AT91_EMAC_RX_BUFF_SIZE);
  HAL_DCACHE_INVALIDATE(priv->rb[priv->rx_tail_idx].rb, rb_len);

  // For all scatter-gather buffers that we need to fill.
  while (sg_idx < sg_len) {
    copy_cnt = MIN(sg_list[sg_idx].len - sg_offset, rb_len - rb_offset);

    if (copy_cnt && sg_list[sg_idx].buf)
      memcpy(((cyg_uint8 *)sg_list[sg_idx].buf) + sg_offset,
             &priv->rb[priv->rx_tail_idx].rb[rb_offset],
             copy_cnt);

    // Update number of bytes remaining in frame, exit if complete.
    priv->rx_frame_cnt -= copy_cnt;
    if (!priv->rx_frame_cnt)
      break;

    // If sg buffer is completely filled, move to next
    sg_offset += copy_cnt;
    if (sg_offset == sg_list[sg_idx].len) {
      sg_idx++;
      sg_offset = 0;
    }

    // If rx buffer is completely drained, move to next
    rb_offset += copy_cnt;
    if (rb_offset == rb_len) {
      AT91_ETH_NEXT_RBD(priv->rx_tail_idx);
      rb_offset = 0;
      // Get count of bytes in this buffer
      rb_len = MIN(priv->rx_frame_cnt, AT91_EMAC_RX_BUFF_SIZE);
      HAL_DCACHE_INVALIDATE(priv->rb[priv->rx_tail_idx].rb, rb_len);
    }
  }
}


// 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 = {
  .int_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 #3: 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] 2+ messages in thread

end of thread, other threads:[~2011-03-01 16:42 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-01 12:26 [ECOS] Scheduler Lock without Kernel Martin Laabs
2011-03-01 16:42 ` Daniel Helgason

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).