if_at91.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,030 行 · 第 1/2 页

C
1,030
字号
//==========================================================================
//
//      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
// Contributors:  
// Date:         2006-05-10
// 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

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

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

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

   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;
   unsigned char enaddr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR};
   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",

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?