Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/drivers/omapl138_eth.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file omapl138_eth.c
 * @brief OMAP-L138 Ethernet MAC controller
 *
 * @section License
 *
 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneTCP Open.
 *
 * This program 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
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL NIC_TRACE_LEVEL

//Dependencies
#include "soc_omapl138.h"
#include "hw_types.h"
#include "hw_syscfg0_omapl138.h"
#include "hw_emac.h"
#include "hw_emac_ctrl.h"
#include "hw_mdio.h"
#include "cache.h"
#include "interrupt.h"
#include "psc.h"
#include "core/net.h"
#include "drivers/omapl138_eth.h"
#include "debug.h"

//MDIO input clock frequency
#define MDIO_INPUT_CLK 75000000
//MDIO output clock frequency
#define MDIO_OUTPUT_CLK 1000000

//Underlying network interface
static NetInterface *nicDriverInterface;

//IAR EWARM compiler?
#if defined(__ICCARM__)

//Transmit buffer
#pragma data_alignment = 4
#pragma location = ".ram_no_cache"
static uint8_t txBuffer[OMAPL138_ETH_TX_BUFFER_COUNT][OMAPL138_ETH_TX_BUFFER_SIZE];
//Receive buffer
#pragma data_alignment = 4
#pragma location = ".ram_no_cache"
static uint8_t rxBuffer[OMAPL138_ETH_RX_BUFFER_COUNT][OMAPL138_ETH_RX_BUFFER_SIZE];
//Transmit buffer descriptors
#pragma data_alignment = 4
#pragma location = ".ram_cppi"
static Omapl138TxBufferDesc txBufferDesc[OMAPL138_ETH_TX_BUFFER_COUNT];
//Receive buffer descriptors
#pragma data_alignment = 4
#pragma location = ".ram_cppi"
static Omapl138RxBufferDesc rxBufferDesc[OMAPL138_ETH_RX_BUFFER_COUNT];

//Keil MDK-ARM or GCC compiler?
#else

//Transmit buffer
static uint8_t txBuffer[OMAPL138_ETH_TX_BUFFER_COUNT][OMAPL138_ETH_TX_BUFFER_SIZE]
   __attribute__((aligned(4), __section__(".ram_no_cache")));
//Receive buffer
static uint8_t rxBuffer[OMAPL138_ETH_RX_BUFFER_COUNT][OMAPL138_ETH_RX_BUFFER_SIZE]
   __attribute__((aligned(4), __section__(".ram_no_cache")));
//Transmit buffer descriptors
static Omapl138TxBufferDesc txBufferDesc[OMAPL138_ETH_TX_BUFFER_COUNT]
   __attribute__((aligned(4), __section__(".ram_cppi")));
//Receive buffer descriptors
static Omapl138RxBufferDesc rxBufferDesc[OMAPL138_ETH_RX_BUFFER_COUNT]
   __attribute__((aligned(4), __section__(".ram_cppi")));

#endif

//Pointer to the current TX buffer descriptor
static Omapl138TxBufferDesc *txCurBufferDesc;
//Pointer to the current RX buffer descriptor
static Omapl138RxBufferDesc *rxCurBufferDesc;


/**
 * @brief OMAP-L138 Ethernet MAC driver
 **/

const NicDriver omapl138EthDriver =
{
   NIC_TYPE_ETHERNET,
   ETH_MTU,
   omapl138EthInit,
   omapl138EthTick,
   omapl138EthEnableIrq,
   omapl138EthDisableIrq,
   omapl138EthEventHandler,
   omapl138EthSendPacket,
   omapl138EthSetMulticastFilter,
   omapl138EthUpdateMacConfig,
   omapl138EthWritePhyReg,
   omapl138EthReadPhyReg,
   FALSE,
   TRUE,
   TRUE,
   FALSE
};


/**
 * @brief OMAP-L138 Ethernet MAC initialization
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t omapl138EthInit(NetInterface *interface)
{
   error_t error;
   uint_t channel;
   uint32_t temp;

   //Debug message
   TRACE_INFO("Initializing OMAP-L138 Ethernet MAC...\r\n");

   //Save underlying network interface
   nicDriverInterface = interface;

   //Enable EMAC module
   PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_EMAC,
      PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);

   //Select the interface mode (MII/RMII) and configure pin muxing
   omapl138EthInitGpio(interface);

   //Reset the EMAC control module
   EMAC_CTRL_SOFTRESET_R = EMAC_SOFTRESET_SOFTRESET;
   //Wait for the reset to complete
   while(EMAC_CTRL_SOFTRESET_R & EMAC_SOFTRESET_SOFTRESET);

   //Reset the EMAC module
   EMAC_SOFTRESET_R = EMAC_SOFTRESET_SOFTRESET;
   //Wait for the reset to complete
   while(EMAC_SOFTRESET_R & EMAC_SOFTRESET_SOFTRESET);

   //Calculate the MDC clock divider to be used
   temp = (MDIO_INPUT_CLK / MDIO_OUTPUT_CLK) - 1;

   //Initialize MDIO interface
   MDIO_CONTROL_R = MDIO_CONTROL_ENABLE |
      MDIO_CONTROL_FAULTENB | (temp & MDIO_CONTROL_CLKDIV);

   //PHY transceiver initialization
   error = interface->phyDriver->init(interface);
   //Failed to initialize PHY transceiver?
   if(error)
      return error;

   //Clear the control registers
   EMAC_MACCONTROL_R = 0;
   EMAC_RXCONTROL_R = 0;
   EMAC_TXCONTROL_R = 0;

   //Initialize all 16 header descriptor pointer registers to 0
   for(channel = EMAC_CH0; channel <= EMAC_CH7; channel++)
   {
      //TX head descriptor pointer
      EMAC_TXHDP_R(channel) = 0;
      //TX completion pointer
      EMAC_TXCP_R(channel) = 0;
      //RX head descriptor pointer
      EMAC_RXHDP_R(channel) = 0;
      //RX completion pointer
      EMAC_RXCP_R(channel) = 0;
   }

   //Set the upper 32 bits of the source MAC address
   EMAC_MACSRCADDRHI_R = interface->macAddr.b[0] |
      (interface->macAddr.b[1] << 8) |
      (interface->macAddr.b[2] << 16) |
      (interface->macAddr.b[3] << 24);

   //Set the lower 16 bits of the source MAC address
   EMAC_MACSRCADDRLO_R = interface->macAddr.b[4] |
      (interface->macAddr.b[5] << 8);

   //Write the channel number to the MAC index register
   EMAC_MACINDEX_R = EMAC_CH0;

   //Set the upper 32 bits of the source MAC address
   EMAC_MACADDRHI_R = interface->macAddr.b[0] |
      (interface->macAddr.b[1] << 8) |
      (interface->macAddr.b[2] << 16) |
      (interface->macAddr.b[3] << 24);

   //Set the lower 16 bits of the source MAC address
   temp = interface->macAddr.b[4] |
      (interface->macAddr.b[5] << 8);

   //Use the current MAC address to match incoming packet addresses
   EMAC_MACADDRLO_R = EMAC_MACADDRLO_VALID | EMAC_MACADDRLO_MATCHFILT |
         (EMAC_CH0 << EMAC_MACADDRLO_CHANNEL_SHIFT) | temp;

   //Be sure to program all eight MAC address registers, whether the
   //receive channel is to be enabled or not
   for(channel = EMAC_CH1; channel <= EMAC_CH7; channel++)
   {
      //Write the channel number to the MAC index register
      EMAC_MACINDEX_R = channel;
      //The MAC address is not valid
      EMAC_MACADDRLO_R = (channel << EMAC_MACADDRLO_CHANNEL_SHIFT);
   }

   //Clear the MAC address hash registers
   EMAC_MACHASH1_R = 0;
   EMAC_MACHASH2_R = 0;

   //The RX buffer offset must be initialized to zero
   EMAC_RXBUFFEROFFSET_R = 0;

   //Clear all unicast channels
   EMAC_RXUNICASTCLEAR_R = 0xFF;

   //Accept unicast frames
   EMAC_RXUNICASTSET_R |= (1 << EMAC_CH0);

   //Received CRC is transferred to memory for all channels
   EMAC_RXMBPENABLE_R = EMAC_RXMBPENABLE_RXPASSCRC;

   //Accept broadcast frames
   EMAC_RXMBPENABLE_R |= EMAC_RXMBPENABLE_RXBROADEN |
      (EMAC_CH0 << EMAC_RXMBPENABLE_RXBROADCH_SHIFT);

   //Accept hash matching multicast frames
   EMAC_RXMBPENABLE_R |= EMAC_RXMBPENABLE_RXMULTEN |
      (EMAC_CH0 << EMAC_RXMBPENABLE_RXMULTCH_SHIFT);

   //Register interrupt handlers
   IntRegister(SYS_INT_C0_TX, omapl138EthTxIrqHandler);
   IntRegister(SYS_INT_C0_RX, omapl138EthRxIrqHandler);

   //Set the channel number for the TX interrupt
   IntChannelSet(SYS_INT_C0_TX, OMAPL138_ETH_TX_IRQ_CHANNEL);
   //Set the channel number for the RX interrupt
   IntChannelSet(SYS_INT_C0_RX, OMAPL138_ETH_RX_IRQ_CHANNEL);

   //Clear all unused channel interrupt bits
   EMAC_TXINTMASKCLEAR_R = 0xFF;
   EMAC_RXINTMASKCLEAR_R = 0xFF;

   //Enable the receive and transmit channel interrupt bits
   EMAC_TXINTMASKSET_R = (1 << EMAC_CH0);
   EMAC_RXINTMASKSET_R = (1 << EMAC_CH0);

   //Configure TX and RX buffer descriptors
   omapl138EthInitBufferDesc(interface);

   //Write the RX DMA head descriptor pointer
   EMAC_RXHDP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;

   //Enable the receive and transmit DMA controllers
   EMAC_TXCONTROL_R = EMAC_TXCONTROL_TXEN;
   EMAC_RXCONTROL_R = EMAC_RXCONTROL_RXEN;

   //Enable TX and RX
   EMAC_MACCONTROL_R = EMAC_MACCONTROL_GMIIEN;

   //Enable TX and RX completion interrupts
   EMAC_CTRL_CnTXEN_R(EMAC_CORE0) |= (1 << EMAC_CH0);
   EMAC_CTRL_CnRXEN_R(EMAC_CORE0) |= (1 << EMAC_CH0);

   //Accept any packets from the upper layer
   osSetEvent(&interface->nicTxEvent);

   //Successful initialization
   return NO_ERROR;
}


//TMDSLCDK138 board?
#if defined(USE_TMDSLCDK138)

/**
 * @brief GPIO configuration
 * @param[in] interface Underlying network interface
 **/

void omapl138EthInitGpio(NetInterface *interface)
{
   uint32_t temp;

   //Enable GPIO module
   PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_GPIO,
      PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);

   //Configure MII_TXD0, MII_TXD1, MII_TXD2, MII_TXD3, MII_COL, MII_TXCLK and MII_TXEN
   temp =  SYSCFG0_PINMUX_R(2) & ~(SYSCFG_PINMUX2_PINMUX2_31_28 |
      SYSCFG_PINMUX2_PINMUX2_27_24 | SYSCFG_PINMUX2_PINMUX2_23_20 |
      SYSCFG_PINMUX2_PINMUX2_19_16 | SYSCFG_PINMUX2_PINMUX2_15_12 |
      SYSCFG_PINMUX2_PINMUX2_11_8 | SYSCFG_PINMUX2_PINMUX2_7_4);

    SYSCFG0_PINMUX_R(2) = temp |
       (SYSCFG_PINMUX2_PINMUX2_31_28_MII_TXD0 << SYSCFG_PINMUX2_PINMUX2_31_28_SHIFT) |
      (SYSCFG_PINMUX2_PINMUX2_27_24_MII_TXD1 << SYSCFG_PINMUX2_PINMUX2_27_24_SHIFT) |
      (SYSCFG_PINMUX2_PINMUX2_23_20_MII_TXD2 << SYSCFG_PINMUX2_PINMUX2_23_20_SHIFT) |
      (SYSCFG_PINMUX2_PINMUX2_19_16_MII_TXD3 << SYSCFG_PINMUX2_PINMUX2_19_16_SHIFT) |
      (SYSCFG_PINMUX2_PINMUX2_15_12_MII_COL << SYSCFG_PINMUX2_PINMUX2_15_12_SHIFT) |
      (SYSCFG_PINMUX2_PINMUX2_11_8_MII_TXCLK << SYSCFG_PINMUX2_PINMUX2_11_8_SHIFT) |
      (SYSCFG_PINMUX2_PINMUX2_7_4_MII_TXEN << SYSCFG_PINMUX2_PINMUX2_7_4_SHIFT);

   //Configure MII_RXD0, MII_RXD1, MII_RXD2, MII_RXD3, MII_CRS, MII_RXER, MII_RXDV and RXCLK
   temp = SYSCFG0_PINMUX_R(3) & ~(SYSCFG_PINMUX3_PINMUX3_31_28 |
      SYSCFG_PINMUX3_PINMUX3_27_24 | SYSCFG_PINMUX3_PINMUX3_23_20 |
      SYSCFG_PINMUX3_PINMUX3_19_16 | SYSCFG_PINMUX3_PINMUX3_15_12 |
      SYSCFG_PINMUX3_PINMUX3_11_8 | SYSCFG_PINMUX3_PINMUX3_7_4 |
      SYSCFG_PINMUX3_PINMUX3_3_0);

   SYSCFG0_PINMUX_R(3) = temp |
      (SYSCFG_PINMUX3_PINMUX3_31_28_MII_RXD0 << SYSCFG_PINMUX3_PINMUX3_31_28_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_27_24_MII_RXD1 << SYSCFG_PINMUX3_PINMUX3_27_24_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_23_20_MII_RXD2 << SYSCFG_PINMUX3_PINMUX3_23_20_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_19_16_MII_RXD3 << SYSCFG_PINMUX3_PINMUX3_19_16_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_15_12_MII_CRS << SYSCFG_PINMUX3_PINMUX3_15_12_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_11_8_MII_RXER << SYSCFG_PINMUX3_PINMUX3_11_8_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_7_4_MII_RXDV << SYSCFG_PINMUX3_PINMUX3_7_4_SHIFT) |
      (SYSCFG_PINMUX3_PINMUX3_3_0_MII_RXCLK << SYSCFG_PINMUX3_PINMUX3_3_0_SHIFT);

    //Configure MDIO and MDCLK
    temp = SYSCFG0_PINMUX_R(4) &  ~(SYSCFG_PINMUX4_PINMUX4_3_0 |
       SYSCFG_PINMUX4_PINMUX4_7_4);

    SYSCFG0_PINMUX_R(4) = temp |
       (SYSCFG_PINMUX4_PINMUX4_7_4_MDIO_D << SYSCFG_PINMUX4_PINMUX4_7_4_SHIFT) |
       (SYSCFG_PINMUX4_PINMUX4_3_0_MDIO_CLK << SYSCFG_PINMUX4_PINMUX4_3_0_SHIFT);

   //Select MII interface mode
   SYSCFG0_CFGCHIP3_R &= ~SYSCFG_CFGCHIP3_RMII_SEL;
}

#endif


/**
 * @brief Initialize buffer descriptor lists
 * @param[in] interface Underlying network interface
 **/

void omapl138EthInitBufferDesc(NetInterface *interface)
{
   uint_t i;
   uint_t nextIndex;
   uint_t prevIndex;

   //Initialize TX buffer descriptor list
   for(i = 0; i < OMAPL138_ETH_TX_BUFFER_COUNT; i++)
   {
      //Index of the next buffer
      nextIndex = (i + 1) % OMAPL138_ETH_TX_BUFFER_COUNT;
      //Index of the previous buffer
      prevIndex = (i + OMAPL138_ETH_TX_BUFFER_COUNT - 1) % OMAPL138_ETH_TX_BUFFER_COUNT;

      //Next descriptor pointer
      txBufferDesc[i].word0 = (uint32_t) NULL;
      //Buffer pointer
      txBufferDesc[i].word1 = (uint32_t) txBuffer[i];
      //Buffer offset and buffer length
      txBufferDesc[i].word2 = 0;
      //Status flags and packet length
      txBufferDesc[i].word3 = 0;

      //Form a doubly linked list
      txBufferDesc[i].next = &txBufferDesc[nextIndex];
      txBufferDesc[i].prev = &txBufferDesc[prevIndex];
   }

   //Point to the very first descriptor
   txCurBufferDesc = &txBufferDesc[0];

   //Mark the end of the queue
   txCurBufferDesc->prev->word3 = EMAC_TX_WORD3_SOP |
      EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_EOQ;

   //Initialize RX buffer descriptor list
   for(i = 0; i < OMAPL138_ETH_RX_BUFFER_COUNT; i++)
   {
      //Index of the next buffer
      nextIndex = (i + 1) % OMAPL138_ETH_RX_BUFFER_COUNT;
      //Index of the previous buffer
      prevIndex = (i + OMAPL138_ETH_RX_BUFFER_COUNT - 1) % OMAPL138_ETH_RX_BUFFER_COUNT;

      //Next descriptor pointer
      rxBufferDesc[i].word0 = (uint32_t) &rxBufferDesc[nextIndex];
      //Buffer pointer
      rxBufferDesc[i].word1 = (uint32_t) rxBuffer[i];
      //Buffer offset and buffer length
      rxBufferDesc[i].word2 = OMAPL138_ETH_RX_BUFFER_SIZE;
      //Status flags and packet length
      rxBufferDesc[i].word3 = EMAC_RX_WORD3_OWNER;

      //Form a doubly linked list
      rxBufferDesc[i].next = &rxBufferDesc[nextIndex];
      rxBufferDesc[i].prev = &rxBufferDesc[prevIndex];
   }

   //Point to the very first descriptor
   rxCurBufferDesc = &rxBufferDesc[0];

   //Mark the end of the queue
   rxCurBufferDesc->prev->word0 = (uint32_t) NULL;
}


/**
 * @brief OMAP-L138 Ethernet MAC timer handler
 *
 * This routine is periodically called by the TCP/IP stack to
 * handle periodic operations such as polling the link state
 *
 * @param[in] interface Underlying network interface
 **/

void omapl138EthTick(NetInterface *interface)
{
   //Handle periodic operations
   interface->phyDriver->tick(interface);

   //Misqueued buffer condition?
   if(rxCurBufferDesc->word3 & EMAC_RX_WORD3_OWNER)
   {
      if(EMAC_RXHDP_R(EMAC_CH0) == 0)
      {
         //The host acts on the misqueued buffer condition by writing the added
         //buffer descriptor address to the appropriate RX DMA head descriptor
         //pointer
         EMAC_RXHDP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;
      }
   }
}


/**
 * @brief Enable interrupts
 * @param[in] interface Underlying network interface
 **/

void omapl138EthEnableIrq(NetInterface *interface)
{
   //Enable Ethernet MAC interrupts
   IntSystemEnable(SYS_INT_C0_TX);
   IntSystemEnable(SYS_INT_C0_RX);

   //Enable Ethernet PHY interrupts
   interface->phyDriver->enableIrq(interface);
}


/**
 * @brief Disable interrupts
 * @param[in] interface Underlying network interface
 **/

void omapl138EthDisableIrq(NetInterface *interface)
{
   //Disable Ethernet MAC interrupts
   IntSystemDisable(SYS_INT_C0_TX);
   IntSystemDisable(SYS_INT_C0_RX);

   //Disable Ethernet PHY interrupts
   interface->phyDriver->disableIrq(interface);
}


/**
 * @brief Ethernet MAC transmit interrupt
 **/

void omapl138EthTxIrqHandler(void)
{
   bool_t flag;
   uint32_t status;
   uint32_t temp;
   Omapl138TxBufferDesc *p;

   //Enter interrupt service routine
   osEnterIsr();

   //This flag will be set if a higher priority task must be woken
   flag = FALSE;

   //Clear system interrupt status
   IntSystemStatusClear(SYS_INT_C0_TX);

   //Read the C0TXSTAT register to determine which channels caused the interrupt
   status = EMAC_CTRL_C0TXSTAT_R;

   //Packet transmitted on channel 0?
   if(status & (1 << EMAC_CH0))
   {
      //Point to the buffer descriptor
      p = (Omapl138TxBufferDesc *) EMAC_TXCP_R(EMAC_CH0);

      //Read the status flags
      temp = p->word3 & (EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP |
         EMAC_TX_WORD3_OWNER | EMAC_TX_WORD3_EOQ);

      //Misqueued buffer condition?
      if(temp == (EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_EOQ))
      {
         //Check whether the next descriptor pointer is non-zero
         if(p->word0 != 0)
         {
            //The host corrects the misqueued buffer condition by writing the
            //misqueued packet’s buffer descriptor address to the appropriate
            //TX DMA head descriptor pointer
            EMAC_TXHDP_R(EMAC_CH0) = (uint32_t) p->word0;
         }
      }

      //Write the TX completion pointer
      EMAC_TXCP_R(EMAC_CH0) = (uint32_t) p;

      //Check whether the TX buffer is available for writing
      if(!(txCurBufferDesc->word3 & EMAC_TX_WORD3_OWNER))
      {
         //Notify the TCP/IP stack that the transmitter is ready to send
         flag |= osSetEventFromIsr(&nicDriverInterface->nicTxEvent);
      }
   }

   //Writes the DMA end of interrupt vector
   EMAC_MACEOIVECTOR_R = EMAC_MACEOIVECTOR_C0TX;

   //Leave interrupt service routine
   osExitIsr(flag);
}


/**
 * @brief Ethernet MAC receive interrupt
 **/

void omapl138EthRxIrqHandler(void)
{
   bool_t flag;
   uint32_t status;

   //Enter interrupt service routine
   osEnterIsr();

   //This flag will be set if a higher priority task must be woken
   flag = FALSE;

   //Clear system interrupt status
   IntSystemStatusClear(SYS_INT_C0_RX);

   //Read the C0RXSTAT register to determine which channels caused the interrupt
   status = EMAC_CTRL_C0RXSTAT_R;

   //Packet received on channel 0?
   if(status & (1 << EMAC_CH0))
   {
      //Disable RX interrupts
      EMAC_CTRL_CnRXEN_R(EMAC_CORE0) &= ~(1 << EMAC_CH0);

      //Set event flag
      nicDriverInterface->nicEvent = TRUE;
      //Notify the TCP/IP stack of the event
      flag |= osSetEventFromIsr(&netEvent);
   }

   //Writes the DMA end of interrupt vector
   EMAC_MACEOIVECTOR_R = EMAC_MACEOIVECTOR_C0RX;

   //Leave interrupt service routine
   osExitIsr(flag);
}


/**
 * @brief OMAP-L138 Ethernet MAC event handler
 * @param[in] interface Underlying network interface
 **/

void omapl138EthEventHandler(NetInterface *interface)
{
   error_t error;

   //Process all pending packets
   do
   {
      //Read incoming packet
      error = omapl138EthReceivePacket(interface);

      //No more data in the receive buffer?
   } while(error != ERROR_BUFFER_EMPTY);

   //Re-enable RX interrupts
   EMAC_CTRL_CnRXEN_R(EMAC_CORE0) |= (1 << EMAC_CH0);
}


/**
 * @brief Send a packet
 * @param[in] interface Underlying network interface
 * @param[in] buffer Multi-part buffer containing the data to send
 * @param[in] offset Offset to the first data byte
 * @return Error code
 **/

error_t omapl138EthSendPacket(NetInterface *interface,
   const NetBuffer *buffer, size_t offset)
{
   size_t length;
   uint32_t temp;

   //Retrieve the length of the packet
   length = netBufferGetLength(buffer) - offset;

   //Check the frame length
   if(length > OMAPL138_ETH_TX_BUFFER_SIZE)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
      //Report an error
      return ERROR_INVALID_LENGTH;
   }

   //Make sure the current buffer is available for writing
   if(txCurBufferDesc->word3 & EMAC_TX_WORD3_OWNER)
      return ERROR_FAILURE;

   //Mark the end of the queue with a NULL pointer
   txCurBufferDesc->word0 = (uint32_t) NULL;

   //Copy user data to the transmit buffer
   netBufferRead((uint8_t *) txCurBufferDesc->word1, buffer, offset, length);

   //Set the length of the buffer
   txCurBufferDesc->word2 = length & EMAC_TX_WORD2_BUFFER_LENGTH;

   //Give the ownership of the descriptor to the DMA
   txCurBufferDesc->word3 = EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP |
      EMAC_TX_WORD3_OWNER | (length & EMAC_TX_WORD3_PACKET_LENGTH);

   //Link the current descriptor to the previous descriptor
   txCurBufferDesc->prev->word0 = (uint32_t) txCurBufferDesc;

   //Read the status flags of the previous descriptor
   temp = txCurBufferDesc->prev->word3 & (EMAC_TX_WORD3_SOP |
      EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_OWNER | EMAC_TX_WORD3_EOQ);

   //Misqueued buffer condition?
   if(temp == (EMAC_TX_WORD3_SOP | EMAC_TX_WORD3_EOP | EMAC_TX_WORD3_EOQ))
   {
      //Clear the misqueued buffer condition
      txCurBufferDesc->prev->word3 = 0;

      //The host corrects the misqueued buffer condition by writing the
      //misqueued packet’s buffer descriptor address to the appropriate
      //TX DMA head descriptor pointer
      EMAC_TXHDP_R(EMAC_CH0) = (uint32_t) txCurBufferDesc;
   }

   //Point to the next descriptor in the list
   txCurBufferDesc = txCurBufferDesc->next;

   //Check whether the next buffer is available for writing
   if(!(txCurBufferDesc->word3 & EMAC_TX_WORD3_OWNER))
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
   }

   //Data successfully written
   return NO_ERROR;
}


/**
 * @brief Receive a packet
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t omapl138EthReceivePacket(NetInterface *interface)
{
   static uint8_t buffer[OMAPL138_ETH_RX_BUFFER_SIZE];
   error_t error;
   size_t n;
   uint32_t temp;

   //The current buffer is available for reading?
   if(!(rxCurBufferDesc->word3 & EMAC_RX_WORD3_OWNER))
   {
      //SOP and EOP flags should be set
      if((rxCurBufferDesc->word3 & EMAC_RX_WORD3_SOP) &&
         (rxCurBufferDesc->word3 & EMAC_RX_WORD3_EOP))
      {
         //Make sure no error occurred
         if(!(rxCurBufferDesc->word3 & EMAC_RX_WORD3_ERROR_MASK))
         {
            //Retrieve the length of the frame
            n = rxCurBufferDesc->word3 & EMAC_RX_WORD3_PACKET_LENGTH;
            //Limit the number of data to read
            n = MIN(n, OMAPL138_ETH_RX_BUFFER_SIZE);

            //Copy data from the receive buffer
            memcpy(buffer, (uint8_t *) rxCurBufferDesc->word1, n);

            //Valid packet received
            error = NO_ERROR;
         }
         else
         {
            //The received packet contains an error
            error = ERROR_INVALID_PACKET;
         }
      }
      else
      {
         //The packet is not valid
         error = ERROR_INVALID_PACKET;
      }

      //Mark the end of the queue with a NULL pointer
      rxCurBufferDesc->word0 = (uint32_t) NULL;
      //Restore the length of the buffer
      rxCurBufferDesc->word2 = OMAPL138_ETH_RX_BUFFER_SIZE;
      //Give the ownership of the descriptor back to the DMA
      rxCurBufferDesc->word3 = EMAC_RX_WORD3_OWNER;

      //Link the current descriptor to the previous descriptor
      rxCurBufferDesc->prev->word0 = (uint32_t) rxCurBufferDesc;

      //Read the status flags of the previous descriptor
      temp = rxCurBufferDesc->prev->word3 & (EMAC_RX_WORD3_SOP |
         EMAC_RX_WORD3_EOP | EMAC_RX_WORD3_OWNER | EMAC_RX_WORD3_EOQ);

      //Misqueued buffer condition?
      if(temp == (EMAC_RX_WORD3_SOP | EMAC_RX_WORD3_EOP | EMAC_RX_WORD3_EOQ))
      {
         //The host acts on the misqueued buffer condition by writing the added
         //buffer descriptor address to the appropriate RX DMA head descriptor
         //pointer
         EMAC_RXHDP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;
      }

      //Write the RX completion pointer
      EMAC_RXCP_R(EMAC_CH0) = (uint32_t) rxCurBufferDesc;

      //Point to the next descriptor in the list
      rxCurBufferDesc = rxCurBufferDesc->next;
   }
   else
   {
      //No more data in the receive buffer
      error = ERROR_BUFFER_EMPTY;
   }

   //Check whether a valid packet has been received
   if(!error)
   {
      //Pass the packet to the upper layer
      nicProcessPacket(interface, buffer, n);
   }

   //Return status code
   return error;
}


/**
 * @brief Configure multicast MAC address filtering
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t omapl138EthSetMulticastFilter(NetInterface *interface)
{
   uint_t i;
   uint_t k;
   uint8_t *p;
   uint32_t hashTable[2];
   MacFilterEntry *entry;

   //Debug message
   TRACE_DEBUG("Updating OMAP-L138 hash table...\r\n");

   //Clear hash table
   hashTable[0] = 0;
   hashTable[1] = 0;

   //The MAC filter table contains the multicast MAC addresses
   //to accept when receiving an Ethernet frame
   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->macMulticastFilter[i];

      //Valid entry?
      if(entry->refCount > 0)
      {
         //Point to the MAC address
         p = entry->addr.b;

         //Apply the hash function
         k = (p[0] >> 2) ^ (p[0] << 4);
         k ^= (p[1] >> 4) ^ (p[1] << 2);
         k ^= (p[2] >> 6) ^ p[2];
         k ^= (p[3] >> 2) ^ (p[3] << 4);
         k ^= (p[4] >> 4) ^ (p[4] << 2);
         k ^= (p[5] >> 6) ^ p[5];

         //The hash value is reduced to a 6-bit index
         k &= 0x3F;

         //Update hash table contents
         hashTable[k / 32] |= (1 << (k % 32));
      }
   }

   //Write the hash table
   EMAC_MACHASH1_R = hashTable[0];
   EMAC_MACHASH2_R = hashTable[1];

   //Debug message
   TRACE_DEBUG("  MACHASH1 = %08" PRIX32 "\r\n", EMAC_MACHASH1_R);
   TRACE_DEBUG("  MACHASH2 = %08" PRIX32 "\r\n", EMAC_MACHASH2_R);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Adjust MAC configuration parameters for proper operation
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t omapl138EthUpdateMacConfig(NetInterface *interface)
{
   uint32_t config;

   //Read MAC control register
   config = EMAC_MACCONTROL_R;

   //100BASE-TX or 10BASE-T operation mode?
   if(interface->linkSpeed == NIC_LINK_SPEED_100MBPS)
      config |= EMAC_MACCONTROL_RMIISPEED;
   else
      config &= ~EMAC_MACCONTROL_RMIISPEED;

   //Half-duplex or full-duplex mode?
   if(interface->duplexMode == NIC_FULL_DUPLEX_MODE)
      config |= EMAC_MACCONTROL_FULLDUPLEX;
   else
      config &= ~EMAC_MACCONTROL_FULLDUPLEX;

   //Update MAC control register
   EMAC_MACCONTROL_R = config;

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Write PHY register
 * @param[in] phyAddr PHY address
 * @param[in] regAddr Register address
 * @param[in] data Register value
 **/

void omapl138EthWritePhyReg(uint8_t phyAddr, uint8_t regAddr, uint16_t data)
{
   uint32_t value;

   //Set up a write operation
   value = MDIO_USERACCESS0_GO | MDIO_USERACCESS0_WRITE;
   //PHY address
   value |= (phyAddr << MDIO_USERACCESS0_PHYADR_SHIFT) & MDIO_USERACCESS0_PHYADR;
   //Register address
   value |= (regAddr << MDIO_USERACCESS0_REGADR_SHIFT) & MDIO_USERACCESS0_REGADR;
   //Register value
   value |= data & MDIO_USERACCESS0_DATA;

   //Start a write operation
   MDIO_USERACCESS0_R = value;
   //Wait for the write to complete
   while(MDIO_USERACCESS0_R & MDIO_USERACCESS0_GO);
}


/**
 * @brief Read PHY register
 * @param[in] phyAddr PHY address
 * @param[in] regAddr Register address
 * @return Register value
 **/

uint16_t omapl138EthReadPhyReg(uint8_t phyAddr, uint8_t regAddr)
{
   uint32_t value;

   //Set up a read operation
   value = MDIO_USERACCESS0_GO | MDIO_USERACCESS0_READ;
   //PHY address
   value |= (phyAddr << MDIO_USERACCESS0_PHYADR_SHIFT) & MDIO_USERACCESS0_PHYADR;
   //Register address
   value |= (regAddr << MDIO_USERACCESS0_REGADR_SHIFT) & MDIO_USERACCESS0_REGADR;

   //Start a read operation
   MDIO_USERACCESS0_R = value;
   //Wait for the read to complete
   while(MDIO_USERACCESS0_R & MDIO_USERACCESS0_GO);

   //Return PHY register contents
   return MDIO_USERACCESS0_R & MDIO_USERACCESS0_DATA;
}