/**
 * @file - cpswif.c
 * lwIP Ethernet interface for CPSW port
 *
 */

/**
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/**
 * Copyright (c) 2010 Texas Instruments Incorporated
 *
 * This file is dervied from the "ethernetif.c" skeleton Ethernet network
 * interface driver for lwIP.
 */
#include <semaphore.h>
#include <bsp.h>
#include <sched.h>

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "netif/etharp.h"
#include "netif/ppp/pppoe.h"
#include "lwip/err.h"
#include "netif/cpswif.h"
#include "arch/cc.h"

/* DriverLib Header Files required for this interface driver. */
#include "cpsw.h"
#include "mdio.h"
#include "delay.h"
#include "phy.h"
#include "cache.h"

#include "lwiplib.h"

/* CPPI RAM size in bytes */
#ifndef SIZE_CPPI_RAM
#define SIZE_CPPI_RAM                            0x2000
#endif

#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))

#define PORT_1                                   0x0
#define PORT_2                                   0x1
#define PORT_0_MASK                              0x1
#define PORT_1_MASK                              0x2
#define PORT_2_MASK                              0x4
#define HOST_PORT_MASK                           PORT_0_MASK
#define SLAVE_PORT_MASK(slv_port_num)            (1 << slv_port_num)
#define PORT_MASK                                (0x7)
#define INDV_PORT_MASK(slv_port_num)             (1 << slv_port_num)

#define ENTRY_TYPE                               0x30
#define ENTRY_TYPE_IDX                           7
#define ENTRY_FREE                               0

/* MDIO input and output frequencies in Hz */
#define MDIO_FREQ_INPUT                          125000000
#define MDIO_FREQ_OUTPUT                         1000000

#define CPDMA_BUF_DESC_OWNER                     0x20000000
#define CPDMA_BUF_DESC_SOP                       0x80000000
#define CPDMA_BUF_DESC_EOP                       0x40000000
#define CPDMA_BUF_DESC_EOQ                       0x10000000
#define CPDMA_BUF_DESC_FROM_PORT                 0x70000
#define CPDMA_BUF_DESC_FROM_PORT_SHIFT           16
#define CPDMA_BUF_DESC_TO_PORT(port)             ((port << 16) | 0x100000)
#define CPDMA_BD_LEN_MASK                        0xFFFF
#define CPDMA_BD_PKTLEN_MASK                     0xFFFF

#define MAX_TRANSFER_UNIT                        1500
#define PBUF_LEN_MAX                             1520

#define MIN_PKT_LEN                              60

/* Define those to better describe the network interface. */
#define IFNAME0                                  'e'
#define IFNAME1                                  'n'

#define MASK_LOWER_4BITS_BYTE                    (0x0F)
#define MASK UPPER_4BITS_BYTE                    (0xF0)

#define MASK_BROADCAST_ADDR                      (0xFF)
#define MASK_MULTICAST_ADDR                      (0x01)

#define ALE_ENTRY_VLAN                           0x20
#define ALE_ENTRY_VLANUCAST                      0x30
#define ALE_ENTRY_UCAST                          0x10
#define ALE_ENTRY_MCAST                          0xD0
#define ALE_ENTRY_OUI                            (0x80)
#define ALE_ENTRY_ADDR                           (0x10)
#define ALE_ENTRY_VLAN_ADDR                      (0x30)
#define ALE_VLAN_ENTRY_MEMBER_LIST               0
#define ALE_VLAN_ENTRY_FRC_UNTAG_EGR             3
#define ALE_VLAN_ENTRY_MCAST_UNREG               (1)
#define ALE_VLAN_ENTRY_MCAST_REG                 (2)
#define ALE_VLAN_ENTRY_ID                        (3)
#define ALE_VLAN_ID_MASK                         (0x0FFF)
#define ALE_VLAN_ENTRY_ID_BIT0_BIT7              6
#define ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11        7
#define ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN  (0x08)
#define ALE_VLANUCAST_ENTRY_ID_BIT0_BIT7         6
#define ALE_VLANUCAST_ENTRY_TYPE_ID_BIT8_BIT11   7
#define ALE_UCAST_ENTRY_TYPE                     7
#define ALE_UCAST_TYPE_MASK                      (0xC0)
#define ALE_UCAST_TYPE_SHIFT                     (6)
#define ALE_UCAST_TYPE_PERSISTANT                (0x00)
#define ALE_UCAST_TYPE_UNTOUCHED                 (0x40)
#define ALE_UCAST_TYPE_OUI                       (0x80)
#define ALE_UCAST_TYPE_TOUCHED                   (0xC0)
#define ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC         8
#define ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK         (0x03)
#define ALE_UCAST_ENTRY_PORT_SHIFT               2
#define ALE_MCAST_ENTRY_TYPE_FWD_STATE           7
#define ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT     (6)
#define ALE_MCAST_ENTRY_PORTMASK_SUP             8
#define ALE_MCAST_ENTRY_SUPER_MASK               (0x02)
#define ALE_MCAST_ENTRY_SUPER_SHIFT              (1)
#define ALE_MCAST_ENTRY_PORT_MASK                (0x1C)
#define ALE_MCAST_ENTRY_PORTMASK_SHIFT           2

#define SELECT_10_HALF                          (1 << 0)
#define SELECT_10_FULL                          (1 << 1)
#define SELECT_100_HALF                         (1 << 2)
#define SELECT_100_FULL                         (1 << 3)
#define SELECT_1000_HALF                        (1 << 4)
#define SELECT_1000_FULL                        (1 << 5)

#define SELECT_SPEED_10                         (0)
#define SELECT_SPEED_100                        (1)
#define SELECT_SPEED_1000                       (2)

#define SELECT_FORCED                           (0)
#define SELECT_AUTONEG                          (1)
#define SELECT_BOTH                             (2)

#define SELECT_HALF_DUPLEX                      (0)
#define SELECT_FULL_DUPLEX                      (1)


#define SEM_DEFAULT_PSHARED (0)
#define SEM_INITIAL_VALUE (0)
#define RX_PRIORITY   (0)
#define TX_PRIORITY   (0)

/**
 * RX thread stack size.
 */
#define RX_THREAD_STACKSIZE 2048

 /**
 * RX thread stack size.
 */
#define TX_THREAD_STACKSIZE 2048

static void rx_thread_function(void* arg);
static void tx_thread_function(void* arg);

/* TX Buffer descriptor data structure */
struct cpdma_tx_bd {
  volatile struct cpdma_tx_bd *next;
  volatile u32_t bufptr;
  volatile u32_t bufoff_len;
  volatile u32_t flags_pktlen;

  /* helper to know which pbuf this tx bd corresponds to */
  volatile struct pbuf *pbuf;
}cpdma_tx_bd;

/* RX Buffer descriptor data structure */
struct cpdma_rx_bd {
  volatile struct cpdma_rx_bd *next;
  volatile u32_t bufptr;
  volatile u32_t bufoff_len;
  volatile u32_t flags_pktlen;

  /* helper to know which pbuf this rx bd corresponds to */
  volatile struct pbuf *pbuf;
}cpdma_rx_bd;

/**
 * Helper struct to hold the data used to operate on the receive
 * buffer descriptor ring
 */
struct rxch {
  /* The head of the bd chain which can be allocated for reception*/
  volatile struct cpdma_rx_bd *free_head;

  /* The head of the bd chain which is receiving data */
  volatile struct cpdma_rx_bd *recv_head;

  /* The tail of the bd chain which is receiving data */
  volatile struct cpdma_rx_bd *recv_tail;

  /* The number of free bd's, which can be allocated for reception */
  volatile u32_t free_num;
}rxch;

/**
 * Helper struct to hold the data used to operate on the transmit
 * buffer descriptor ring
 */
struct txch {
  /* The bd which is free to send */
  volatile struct cpdma_tx_bd *free_head;

  /* The head of the bd chain, being sent */
  volatile struct cpdma_tx_bd *send_head;

  /* The tail of the bd chain, being sent */
  volatile struct cpdma_tx_bd *send_tail;

  /* The number of free bd's, which can be sent */
  volatile u32_t free_num;
}txch;

volatile struct cpdma_tx_bd *free_head;

/**
 * Slave port information
 */
struct cpswport{
  u32_t port_base;
  u32_t sliver_base;
  u32_t phy_addr;

  /* The PHY is capable of GitaBit or Not */
  u32_t phy_gbps;
}cpswport;

/**
 * CPSW instance information
 */
struct cpswinst{
  /* Base addresses */
  u32_t ss_base;
  u32_t mdio_base;
  u32_t wrpr_base;
  u32_t ale_base;
  u32_t cpdma_base;
  u32_t cppi_ram_base;
  u32_t host_port_base;

  /* Slave port information */
  struct cpswport port[MAX_SLAVEPORT_PER_INST];

  /* The tx/rx channels for the interface */
  struct txch txch;
  struct rxch rxch;
  sys_thread_t RxThread; /**< RX receive thread data object pointer */
  sem_t rxsem;
  sys_thread_t TxThread; /**< RX receive thread data object pointer */
  sem_t txsem;
  sys_mutex_t txmtx;
}cpswinst;

/* Defining set of CPSW base addresses for all the instances */
static struct cpswinst cpsw_inst_data[MAX_CPSW_INST];

/**
* Function to setup the instance parameters inside the interface
* @param  cpswif  The interface structure pointer
* @return None.
*/
static void
cpswif_inst_config(struct cpswportif *cpswif) {
  u32_t inst_num = cpswif->inst_num;
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];

  /**
   * Code is added for only instance 0. If more instances
   * are there, assign base addresses and phy info here
   */
  if(0 == inst_num) {
    cpswinst->ss_base = CPSW0_SS_REGS;
    cpswinst->mdio_base = CPSW0_MDIO_REGS;
    cpswinst->wrpr_base = CPSW0_WR_REGS;
    cpswinst->cpdma_base = CPSW0_CPDMA_REGS;
    cpswinst->ale_base = CPSW0_ALE_REGS;
    cpswinst->cppi_ram_base = CPSW0_CPPI_RAM_REGS;
    cpswinst->host_port_base = CPSW0_PORT_0_REGS;
    cpswinst->port[PORT_1].port_base = CPSW0_PORT_1_REGS;
    cpswinst->port[PORT_1].sliver_base = CPSW0_SLIVER_1_REGS;
#ifdef CPSW0_PORT_1_PHY_ADDR
    cpswinst->port[PORT_1].phy_addr = CPSW0_PORT_1_PHY_ADDR;
    cpswinst->port[PORT_1].phy_gbps = CPSW0_PORT_1_PHY_GIGABIT;
#endif
    cpswinst->port[PORT_2].port_base = CPSW0_PORT_2_REGS;
    cpswinst->port[PORT_2].sliver_base = CPSW0_SLIVER_2_REGS;
#ifdef CPSW0_PORT_2_PHY_ADDR
    cpswinst->port[PORT_2].phy_addr = CPSW0_PORT_2_PHY_ADDR;
    cpswinst->port[PORT_2].phy_gbps = CPSW0_PORT_2_PHY_GIGABIT;
#endif
  }
}

/**
 * Gives the index of the ALE entry which is free
 * @param  cpswinst  The CPSW instance structure pointer
 *
 * @return index of the ALE entry which is free
 *         ERR_VAL if entry not found
 */
static err_t
cpswif_ale_entry_match_free(struct cpswinst *cpswinst) {
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS];
  s32_t idx;

  /* Check which ALE entry is free starting from 0th entry */
  for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) {
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry);

    /* Break if the table entry is free */
    if (((*(((u8_t *)ale_entry) + ENTRY_TYPE_IDX))
         & ENTRY_TYPE) == ENTRY_FREE) {
       return idx;
    }
  }

  return ERR_VAL;
}

#ifdef CPSW_DUAL_MAC_MODE
/**
 * Sets the VLAN and VLAN/UCAST entries in ALE table for Dual Mac mode
 * @param cpswinst   The CPSW instance structure pointer
 * @param port_num   The slave port number
 * @param eth_addr   Ethernet address
 *
 * @return None
 */
static void
cpswif_port_to_host_vlan_cfg(struct cpswinst *cpswinst, u32_t port_num,
                             u8_t *eth_addr)
{
  s32_t idx;
  u32_t cnt;
  u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};
  u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_free(cpswinst);

  if(MAX_ALE_ENTRIES == idx) {
    return;
  }

  /* Set up the VLAN Entry */
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) =
                                   HOST_PORT_MASK | SLAVE_PORT_MASK(port_num);

  /**
   * Set the bit fields for entry type and VLAN ID. Set the port
   * number as VLAN ID. So only lsb 2 bits of VLAN_ID field will be used.
   */
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = port_num;
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = ALE_ENTRY_VLAN;

  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_FRC_UNTAG_EGR) =
                                   HOST_PORT_MASK | SLAVE_PORT_MASK(port_num);

  /* Set the VLAN entry in the ALE table */
  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry);

  idx = cpswif_ale_entry_match_free(cpswinst);

  if(MAX_ALE_ENTRIES == idx) {
    return;
  }

  /* Set up the VLAN/Unicast Entry */
  for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_vu_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  *(((u8_t *)ale_vu_entry) + ALE_VLANUCAST_ENTRY_TYPE_ID_BIT8_BIT11) =
                                                           ALE_ENTRY_VLANUCAST;
  *(((u8_t *)ale_vu_entry) + ALE_VLANUCAST_ENTRY_ID_BIT0_BIT7) = port_num;

  /* Set the VLAN/Unicast entry in the ALE table */
  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry);
}

#else /* CPSW_DUAL_MAC_MODE */

/**
 * Sets a unicast entry in the ALE table.
 * @param cpswinst   The CPSW instance structure pointer
 * @param port_num   The slave port number
 * @param eth_addr   Ethernet address
 *
 * @return None
 */
static void
cpswif_ale_unicastentry_set(struct cpswinst *cpswinst, u32_t port_num,
                            u8_t *eth_addr) {
  volatile u32_t cnt;
  volatile s32_t idx;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_UCAST;
  *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) =
                                   (port_num << ALE_UCAST_ENTRY_PORT_SHIFT);

  idx = cpswif_ale_entry_match_free(cpswinst);

  if (idx < MAX_ALE_ENTRIES ) {
    CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);
  }
}

/**
 * Sets a multicast entry in the ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param portmask   The port mask for the port number
 * @param eth_addr   Ethernet Address
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static void
cpswif_ale_multicastentry_set(struct cpswinst *cpswinst, u32_t portmask,
                              u8_t *eth_addr)
{
  volatile u32_t cnt;
  volatile s32_t idx;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_free(cpswinst);
  if (idx < MAX_ALE_ENTRIES ) {
    for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
      *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
    }

    *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ALE_ENTRY_MCAST;
    *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |=
                                (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT);

    CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);
  }
}
#endif /* CPSW_DUAL_MAC_MODE */

#ifdef CPSW_SWITCH_CONFIG
/**
 * Checks the address is value for given type
 *
 * @param   eth_addr   Ethernet Address
 * @param   addr_type  Type of address (Unicast, Multicast, Broadcast)
 *
 * @return  the status
 */
static u32_t
check_valid_addr(u8_t *eth_addr, u32_t addr_type) {
  u32_t cnt = 0;

  if (!addr_type)
    return TRUE;

  for (cnt = 0; cnt < LEN_MAC_ADDRESS; cnt++)
    if ((eth_addr[cnt] & MASK_BROADCAST_ADDR) != MASK_BROADCAST_ADDR)
      break;

  if ((cnt == LEN_MAC_ADDRESS) && (addr_type == ADDR_TYPE_BROADCAST))
    return TRUE;
  else if (cnt == LEN_MAC_ADDRESS)
    return FALSE;
  else if (addr_type == ADDR_TYPE_BROADCAST)
    return FALSE;

  if (addr_type == ADDR_TYPE_MULTICAST) {
    if ((eth_addr[0] & MASK_MULTICAST_ADDR) == MASK_MULTICAST_ADDR)
      return TRUE;
  } else if (addr_type == ADDR_TYPE_UNICAST) {
    if ((eth_addr[0] & MASK_MULTICAST_ADDR) != MASK_MULTICAST_ADDR)
      return TRUE;
  }

  return FALSE;
}

/**
 * Gives the index of the ALE entry which is untouched ageable
 * @param  cpswinst  The CPSW instance structure pointer
 *
 * @return index of the ALE entry which is free
 *         ERR_VAL if entry not found
 */
static err_t
cpswif_ale_entry_match_ageable(struct cpswinst *cpswinst) {
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS];
  u32_t type;
  s32_t idx;

  /* Check which ALE entry is free starting from 0th entry */
  for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) {
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry);

    type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE;

    /* Goto next entry if the ale entry is not valid address */
    if ((type != ALE_ENTRY_ADDR) && (type != ALE_ENTRY_VLAN_ADDR))
      continue;

    if (check_valid_addr((u8_t *)ale_entry, ADDR_TYPE_MULTICAST))
      continue;

    type = *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) & ALE_UCAST_TYPE_MASK;

    if ((type != ALE_UCAST_TYPE_PERSISTANT) && (type != ALE_UCAST_TYPE_OUI))
       return idx;
    }

  return ERR_VAL;
}

/**
 * Configure the rate limit for packets transmit of ports
 * @param  cpswinst    The CPSW instance
 * @param  enable      Enable Rate Limit
 * @param  direction   Select TX or RX mode
 * @param  port_num    Port Number
 * @param  addr_type   Type of Address (Broadcast or Multicast)
 * @param  limit       Vale of rate limit to be set
 *
 * @return  None
 *
 **/
static void
cpswif_rate_limit(struct cpswinst *cpswinst, u32_t enable, u32_t direction,
                  u32_t port_num, u32_t addr_type, u32_t limit) {
  if (!enable) {
    CPSWALERateLimitDisable(cpswinst->ale_base);
  }

  if (addr_type == ADDR_TYPE_BROADCAST) {
    CPSWALEBroadcastRateLimitSet(cpswinst->ale_base, port_num, limit);
  } else if (addr_type == ADDR_TYPE_MULTICAST) {
    CPSWALEMulticastRateLimitSet(cpswinst->ale_base, port_num, limit);
  }

  if (port_num == 0) {
    if (direction) {
      CPSWALERateLimitTXMode(cpswinst->ale_base);
    } else {
      CPSWALERateLimitRXMode(cpswinst->ale_base);
    }
  } else {
    if (direction) {
      CPSWALERateLimitRXMode(cpswinst->ale_base);
    } else {
      CPSWALERateLimitTXMode(cpswinst->ale_base);
    }
  }

  CPSWALERateLimitEnable(cpswinst->ale_base);
}

/**
 * Gives the index of the ALE entry which match address of VLAN
 * @param  cpswinst  The CPSW instance structure pointer
 * @param  eth_addr  Ethernet address
 * @param  vid       VLAN ID
 *
 * @return index of the ALE entry which match address of VLAN
 *         ERR_VAL if entry not found
 */
static err_t
cpswif_ale_entry_match_addr(struct cpswinst *cpswinst, u8_t *eth_addr,
                            u32_t vid) {
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS];
  u32_t type, cnt;
  s32_t idx;

  /* Check which ALE entry is free starting from 0th entry */
  for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) {
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry);

    type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE;

    /* Goto next entry if the ale entry is not valid address */
    if ((type != ALE_ENTRY_ADDR) && (type != ALE_ENTRY_VLAN_ADDR))
      continue;

    if ((*(((u16_t *)ale_entry) + ALE_VLAN_ENTRY_ID) & ALE_VLAN_ID_MASK) != vid)
      continue;

    for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
      if (*(((u8_t *)ale_entry) + cnt) != eth_addr[ETHARP_HWADDR_LEN - cnt -1])
        break;

      if (ETHARP_HWADDR_LEN == (cnt + 1))
        return idx;
    }
  }

  return ERR_VAL;
}

/**
 * Gives the index of the ALE entry which match vlan
 * @param  cpswinst  The CPSW instance structure pointer
 * @param  vid  VLAN ID
 *
 * @return index of the ALE entry which match vlan
 *         ERR_VAL if entry not found
 */
static err_t
cpswif_ale_entry_match_vlan(struct cpswinst *cpswinst, u32_t vid) {
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS];
  u32_t type;
  s32_t idx;

  /* Check which ALE entry is free starting from 0th entry */
  for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) {
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry);

    type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE;

    /* Goto next entry if the ale entry is not valid vlan */
    if (type != ALE_ENTRY_VLAN)
      continue;

    if ((*(((u16_t *)ale_entry) + ALE_VLAN_ENTRY_ID) & ALE_VLAN_ID_MASK) == vid)
      return idx;
  }

  return ERR_VAL;
}

/**
 * Adds an unicast entry in the ALE table.
 * @param cpswinst   The CPSW instance structure pointer
 * @param port_num   The slave port number
 * @param eth_addr   Ethernet address
 * @param flags      Flags Settings
 *                   0x1 - Secure
 *                   0x2 - Block
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static err_t
cpswif_ale_unicastentry_add(struct cpswinst *cpswinst, u32_t port_num,
                            u8_t *eth_addr, u32_t flags, u32_t ucast_type) {
  volatile u32_t cnt;
  volatile s32_t idx;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0);

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_free(cpswinst);

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_ageable(cpswinst);

  if (ERR_VAL == idx)
    return ERR_VAL;

  for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_ADDR |
                                           (ucast_type << ALE_UCAST_TYPE_SHIFT);
  *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) =
                                  (port_num << ALE_UCAST_ENTRY_PORT_SHIFT) |
                                   (flags & ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK);

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);

  return idx;
}

/**
 * Adds an OUI entry in the ALE table.
 * @param cpswinst   The CPSW instance structure pointer
 * @param eth_addr   Ethernet address
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static err_t
cpswif_ale_OUI_add(struct cpswinst *cpswinst, u8_t *eth_addr) {
  volatile u32_t cnt;
  volatile s32_t idx;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0);

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_free(cpswinst);

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_ageable(cpswinst);

  if (ERR_VAL == idx)
    return ERR_VAL;

  for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_ADDR | ALE_ENTRY_OUI;

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);

  return idx;
}

/**
 * Deletes an unicast entry in the ALE table.
 * @param cpswinst   The CPSW instance structure pointer
 * @param port_num   The slave port number
 * @param eth_addr   Ethernet address
 *
 * @return index of the ALE entry deleted
 *         ERR_VAL if table entry is not present
 */
static err_t
cpswif_ale_unicastentry_del(struct cpswinst *cpswinst, u32_t port_num,
                            u8_t *eth_addr) {
  volatile s32_t idx;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ENTRY_FREE;

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0);

  if (ERR_VAL == idx)
    return ERR_VAL;

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);

  return idx;
}

/**
 * Adds a multicast entry in the ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param portmask   The port mask for the port number
 * @param eth_addr   Ethernet Address
 * @param super      Supervisory Packet
 * @param mcast_st   Multicast Forward State
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static err_t
cpswif_ale_multicastentry_add(struct cpswinst *cpswinst, u32_t portmask,
                              u8_t *eth_addr, u32_t super, u32_t mcast_st) {
  volatile s32_t idx;
  volatile u32_t cnt;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0);

  if (ERR_VAL == idx) {
    idx = cpswif_ale_entry_match_free(cpswinst);
  } else {
    /* Get the entry in the ALE table */
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry);
  }

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_ageable(cpswinst);

  if (ERR_VAL == idx)
    return ERR_VAL;

  for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  portmask |= (*(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &
                                        ALE_MCAST_ENTRY_PORT_MASK);

  *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) =
                          (ALE_ENTRY_ADDR |
                           (mcast_st << ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT));
  *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &=
                           ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT);
  *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |=
                           (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT) |
                            ((super << ALE_MCAST_ENTRY_SUPER_SHIFT) &
                             ALE_MCAST_ENTRY_SUPER_MASK);

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);

  return idx;
}

/**
 * Deletes a multicast entry in the ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param portmask   The port mask for the port number
 * @param eth_addr   Ethernet Address
 *
 * @return index of the ALE entry deleted
 *         ERR_VAL if table entry is not present
 */
static err_t
cpswif_ale_multicastentry_del(struct cpswinst *cpswinst, u32_t portmask,
                              u8_t *eth_addr) {
  volatile s32_t idx;
  u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0);

  if (ERR_VAL == idx)
    return ERR_VAL;

  CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry);

  if (portmask) {
    *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &=
                             ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT);
    *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) =
                             (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT);
  } else {
    *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE;
  }

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry);

  return idx;
}

/**
 * Adds the VLAN entry in ALE table
 * @param cpswinst    The CPSW instance structure pointer
 * @param vid         VLAN ID
 * @param port_num    The slave port number
 * @param untag       The force untagged egress
 * @param reg_mcast   The registered MCAST Flood Mask
 * @param unreg_mcast The unregistered MCAST Flood Mask
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static err_t
cpswif_ale_vlan_add(struct cpswinst *cpswinst, u32_t vid, u32_t port_num,
                             u32_t untag, u32_t reg_mcast, u32_t unreg_mcast) {
  s32_t idx;
  u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_vlan(cpswinst, vid);

  if (ERR_VAL == idx) {
    idx = cpswif_ale_entry_match_free(cpswinst);
  } else {
    /* Get the entry in the ALE table */
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_v_entry);
  }

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_ageable(cpswinst);

  if (ERR_VAL == idx)
    return ERR_VAL;

  /**
   * Set the bit fields for entry type and VLAN ID.
   */
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid;
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) =
                            ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) &
                              MASK_LOWER_4BITS_BYTE) | ALE_ENTRY_VLAN;
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_FRC_UNTAG_EGR) = untag;
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MCAST_UNREG) = unreg_mcast;
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MCAST_REG) = reg_mcast;
  *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) |=
                                   PORT_MASK & INDV_PORT_MASK(port_num);

  /* Set the VLAN entry in the ALE table */
  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry);

  return idx;
}

/**
 * Dlelete the VLAN entry in ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param vid        VLAN ID
 * @param port_num   The slave port number
 *
 * @return index of the ALE entry deleted
 *         ERR_VAL if table entry is not present
 */
static err_t
cpswif_ale_vlan_del(struct cpswinst *cpswinst, u32_t vid, u32_t port_num) {
  s32_t idx;
  u32_t mask;
  u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_vlan(cpswinst, vid);

  if (ERR_VAL == idx)
    return ERR_VAL;

  CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_v_entry);

  /* Set up the VLAN Entry */
  mask = *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) | PORT_MASK;
  mask &= INDV_PORT_MASK(port_num);

  if (0 == mask)
    return ERR_VAL;

  if (1 == mask) {
      *(((u8_t *)ale_v_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE;
  } else {
    /* Set up the VLAN Entry */
    *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) &=
                              ~INDV_PORT_MASK(port_num);
  }

  /* Set the VLAN/Unicast entry in the ALE table */
  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry);

  return idx;
}

/**
 * Add the VLAN/UCAST entries in ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param vid        VLAN ID
 * @param port_num   The slave port number
 * @param eth_addr   Ethernet address
 * @param flags      Flags Settings
 *                   0x1 - Secure
 *                   0x2 - Block
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static err_t
cpswif_ale_vlan_add_ucast(struct cpswinst *cpswinst, u32_t vid, u32_t port_num,
                             u8_t *eth_addr, u32_t flags, u32_t ucast_type) {
  s32_t idx;
  u32_t cnt;
  u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid);

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_free(cpswinst);

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_ageable(cpswinst);

  if (ERR_VAL == idx)
    return ERR_VAL;

  /**
   * Set the bit fields for entry type and VLAN ID.
   */
  *(((u8_t *)ale_vu_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid;
  *(((u8_t *)ale_vu_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) =
                            ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) &
                              MASK_LOWER_4BITS_BYTE) | ALE_ENTRY_VLAN_ADDR |
                              (ucast_type << ALE_UCAST_TYPE_SHIFT);

  for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_vu_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  *(((u8_t *)ale_vu_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) =
                                  (port_num << ALE_UCAST_ENTRY_PORT_SHIFT) |
                                   (flags & ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK);

  /* Set the VLAN entry in the ALE table */
  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry);

  return idx;
}

/**
 * Dlelete the VLAN/UCAST entry in ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param vid        VLAN ID
 * @param port_num   The slave port number
 *
 * @return index of the ALE entry deleted
 *         ERR_VAL if table entry is not present
 */
static err_t
cpswif_ale_vlan_del_ucast(struct cpswinst *cpswinst, u32_t vid, u32_t port_num,
                             u8_t *eth_addr) {
  volatile s32_t idx;
  u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  *(((u8_t *)ale_vu_entry) + ALE_UCAST_ENTRY_TYPE) = ENTRY_FREE;

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid);

  if (ERR_VAL == idx)
    return ERR_VAL;

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry);

  return idx;
}

/**
 * Add the VLAN/MCAST entries in ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param vid        VLAN ID
 * @param port_num   The slave port number
 * @param eth_addr   Ethernet address
 * @param super      Supervisory Packet
 * @param mcast_st   Multicast Forward State
 *
 * @return index of the ALE entry added
 *         ERR_VAL if table entry is not free
 */
static err_t
cpswif_ale_vlan_add_mcast(struct cpswinst *cpswinst, u32_t vid, u32_t portmask,
                             u8_t *eth_addr, u32_t super, u32_t mcast_st) {
  s32_t idx;
  u32_t cnt;
  u32_t ale_vm_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid);

  if (ERR_VAL == idx) {
    idx = cpswif_ale_entry_match_free(cpswinst);
  } else {
    /* Get the entry in the ALE table */
    CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_vm_entry);
  }

  if (ERR_VAL == idx)
    idx = cpswif_ale_entry_match_ageable(cpswinst);

  if (ERR_VAL == idx)
    return ERR_VAL;

  /**
   * Set the bit fields for entry type and VLAN ID.
   */
  *(((u8_t *)ale_vm_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid;
  *(((u8_t *)ale_vm_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) =
                            ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) &
                              MASK_LOWER_4BITS_BYTE);

  for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) {
    *(((u8_t *)ale_vm_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1];
  }

  portmask |= (*(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &
                                           ALE_MCAST_ENTRY_PORT_MASK);

  *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) |=
                          (ALE_ENTRY_VLAN_ADDR |
                           (mcast_st << ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT));
  *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &=
                              ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT);
  *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |=
                             (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT) |
                              ((super << ALE_MCAST_ENTRY_SUPER_SHIFT) &
                               ALE_MCAST_ENTRY_SUPER_MASK);

  /* Set the VLAN entry in the ALE table */
  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vm_entry);

  return idx;
}

/**
 * Deletes a vlan multicast entry in the ALE table
 * @param cpswinst   The CPSW instance structure pointer
 * @param vid        VLAN ID
 * @param portmask   The port mask for the port number
 * @param eth_addr   Ethernet Address
 *
 * @return index of the ALE entry deleted
 *         ERR_VAL if table entry is not present
 */
static err_t
cpswif_ale_vlan_del_mcast(struct cpswinst *cpswinst, u32_t vid, u32_t portmask,
                              u8_t *eth_addr) {
  volatile s32_t idx;
  u32_t ale_vm_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0};

  idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid);

  if (ERR_VAL == idx)
    return ERR_VAL;

  CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_vm_entry);

  if (portmask) {
    *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &=
                                ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT);
    *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) =
                                (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT);
  } else {
    *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE;
  }

  CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vm_entry);

  return idx;
}

#endif  /* CPSW_SWITCH_CONFIG */

/**
 * AutoNegotiates with phy for link, set it in silver and check for link status.
 * @param  cpswinst   The CPSW instance structure pointer
 * @param  port_num    The slave port number
 * @param  adv         Configuration for advertisement
 *                     SELECT_10_HALF - 10Base Half Duplex
 *                     SELECT_10_FULL - 10Base Full Duplex
 *                     SELECT_100_HALF - 100Base Half Duplex
 *                     SELECT_100_FULL - 100Base Full Duplex
 *                     SELECT_1000_HALF - 1000Base Half Duplex
 *                     SELECT_1000_FULL - 1000Base Full Duplex
 * @return ERR_OK      If link set up is successful
 *                     others if not successful
 */
static err_t
cpswif_phy_autoneg(struct cpswinst *cpswinst, u32_t port_num, u32_t adv) {
  err_t linkstat = ERR_CONN;
  u16_t adv_val = 0, partnr_ablty = 0, gbps_partnr_ablty = 0, gig_adv_val = 0;
  u32_t aut_neg_cnt = 200, auto_stat, transfer_mode = 0;

  /* Check if ethernet PHY is present or not */
  if (0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base)
            & (1 << cpswinst->port[port_num - 1].phy_addr))) {
    LWIP_PRINTF("\n\rNo PHY found at addr %d for Port %d of Instance %d.",
                cpswinst->port[port_num - 1].phy_addr,
                port_num, 0);
    return linkstat;
  }

  LWIP_PRINTF("\n\rPHY found at address %d for  Port %d of Instance %d.",
              cpswinst->port[port_num - 1].phy_addr,
              port_num, 0);

  if (SELECT_1000_HALF == adv) {
    LWIP_PRINTF("\n\rCPSW doesnot support Half Duplex for Gigabyte...");
    return linkstat;
  }

  /* We advertise for 10/100 Mbps both half and full duplex */
  if (adv & SELECT_10_HALF)
    adv_val |= PHY_10BT;
  if (adv & SELECT_10_FULL)
    adv_val |= PHY_10BT_FD;
  if (adv & SELECT_100_HALF)
    adv_val |= PHY_100BTX;
  if (adv & SELECT_100_FULL)
    adv_val |= PHY_100BTX_FD;

  gig_adv_val = 0;
  partnr_ablty = TRUE;
  gbps_partnr_ablty = FALSE;

  /**
   * Not all the PHYs can operate at 1000 Mbps. So advertise only
   * if the PHY is capable
   */
  if (cpswinst->port[port_num -1].phy_gbps) {
    LWIP_PRINTF("\n\rPhy supports Gigabyte...");
    if (adv & SELECT_1000_FULL) {
      gig_adv_val = PHY_1000BT_FD;
      partnr_ablty = TRUE;
      gbps_partnr_ablty = TRUE;
    }
    if (adv & SELECT_1000_HALF) {
      LWIP_PRINTF("\n\rCPSW doesnot support Half Duplex for Gigabyte...");
    }
  } else {
    LWIP_PRINTF("\n\rPhy doesnot support Gigabyte...");
  }

  LWIP_PRINTF("\n\rPerforming Auto-Negotiation...");

  /**
   * Now start Autonegotiation. PHY will talk to its partner
   * and give us what the partner can handle
   */
  if (PhyAutoNegotiate(cpswinst->mdio_base,
                       cpswinst->port[port_num -1].phy_addr,
                       &adv_val, &gig_adv_val) == TRUE) {
    while (aut_neg_cnt) {
      delay(50);
      auto_stat = PhyAutoNegStatusGet(cpswinst->mdio_base,
                                      cpswinst->port[port_num -1].phy_addr);
      if (TRUE == auto_stat) {
        break;
      }
      aut_neg_cnt--;
    }

    if (0 != aut_neg_cnt) {
      linkstat = ERR_OK;
      LWIP_PRINTF("\n\rAuto-Negotiation Successful.");
    } else {
      LWIP_PRINTF("\n\rAuto-Negotiation Not Successful.");
      return ERR_CONN;
    }

    /* Get what the partner supports */
    PhyPartnerAbilityGet(cpswinst->mdio_base,
                         cpswinst->port[port_num -1].phy_addr,
                         &partnr_ablty, &gbps_partnr_ablty);
    if (gbps_partnr_ablty & PHY_LINK_PARTNER_1000BT_FD) {
      LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps.");
      transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX;
    } else {
      if ((adv_val & partnr_ablty) & PHY_100BTX_FD) {
        LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Full Duplex.");
        transfer_mode = CPSW_SLIVER_NON_GIG_FULL_DUPLEX;
      } else if ((adv_val & partnr_ablty) & PHY_100BTX) {
        LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Half Duplex.");
        transfer_mode = CPSW_SLIVER_NON_GIG_HALF_DUPLEX;
      } else if ((adv_val & partnr_ablty) & PHY_10BT_FD) {
        LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Full Duplex.");
        transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_FULL_DUPLEX;
      } else if ((adv_val & partnr_ablty) & PHY_10BT) {
        LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Half Duplex.");
        transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_HALF_DUPLEX;
      } else {
        LWIP_PRINTF("\n\rNo Valid Transfer Mode is detected.");
      }
    }
  } else {
    LWIP_PRINTF("\n\rAuto-Negotiation Not Successful.");
    linkstat = ERR_CONN;
  }

  /**
   * Set the Sliver with the negotiation results if autonegotiation
   * is successful
   */
  if (linkstat == ERR_OK) {
    CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base,
                          transfer_mode);
  }

  /* Check if PHY link is there or not */
  if (FALSE == ((PhyLinkStatusGet(cpswinst->mdio_base,
                cpswinst->port[port_num - 1].phy_addr, 1000)))) {
    LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.",
                port_num, 0);
    return linkstat;
  }

  LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.",
              port_num, 0);

  CPSWSlRGMIIEnable(
                  cpswinst->port[port_num - 1].sliver_base);

  return linkstat;
}

/**
 * Manually configure phy, set it in silver and check for link status.
 * @param  cpswinst   The CPSW instance structure pointer
 * @param  port_num    The slave port number
 * @param  speed       Configuration for speed
 *                     SELECT_SPEED_1000 - 1000 Mbps
 *                     SELECT_SPEED_100 - 100 Mbps
 *                     SELECT_SPEED_10 - 10 Mbps
 * @param  duplex      Configuration for duplex
 *                     SELECT_HALF_DUPLEX - Half Duplex
 *                     SELECT_FULL_DUPLEX - Full Duplex
 * @return ERR_OK      If link set up is successful
 *                     others if not successful
 */
static err_t
cpswif_phy_forced(struct cpswinst *cpswinst, u32_t port_num, u32_t speed,
                  u32_t duplex) {
  err_t linkstat = ERR_CONN;
  u16_t speed_val = 0, duplex_val = 0;
  u32_t frc_stat_cnt = 200, frc_stat = FALSE, transfer_mode = 0;

  /* Check if ethernet PHY is present or not */
  if (0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base)
            & (1 << cpswinst->port[port_num - 1].phy_addr))){
    LWIP_PRINTF("\n\rNo PHY found at addr %d for Port %d of Instance %d.",
                cpswinst->port[port_num - 1].phy_addr,
                port_num, 0);
    return linkstat;
  }

  LWIP_PRINTF("\n\rPHY found at address %d for  Port %d of Instance %d.",
              cpswinst->port[port_num - 1].phy_addr,
              port_num, 0);

  /* configure control for speed and duples */
  if (SELECT_SPEED_1000 == speed)
    speed_val = PHY_SPEED_1000MBPS;
  else if (SELECT_SPEED_100 == speed)
    speed_val = PHY_SPEED_100MBPS;

  if (TRUE == duplex)
    duplex_val = PHY_FULL_DUPLEX;

  if (SELECT_SPEED_1000 == speed) {
    LWIP_PRINTF("\n\rManual Configuration not allowed for Gigabyte...");
    return linkstat;
  }

  if (FALSE == PhyReset(cpswinst->mdio_base,
                        cpswinst->port[port_num - 1].phy_addr)) {
    LWIP_PRINTF("\n\rPHY Reset Failed...");
    return linkstat;
  }

  if (TRUE == (PhyLinkStatusGet(cpswinst->mdio_base,
               cpswinst->port[port_num - 1].phy_addr, 1000))) {
   while (frc_stat_cnt) {
      delay(50);
      /* Check if PHY link is there or not */
      frc_stat = (PhyLinkStatusGet(cpswinst->mdio_base,
                  cpswinst->port[port_num - 1].phy_addr, 1000));

      if (TRUE == frc_stat) {
        LWIP_PRINTF("\n\rPHY Link is Down.");
        break;
      }
      frc_stat_cnt--;
    }
  }

  LWIP_PRINTF("\n\rPerforming Manual Configuration...");

  frc_stat_cnt = 200;
  frc_stat = FALSE;

  if (PhyConfigure(cpswinst->mdio_base, cpswinst->port[port_num -1].phy_addr,
                   speed_val, duplex_val)) {
    while (frc_stat_cnt) {
      delay(50);
      frc_stat = PhyLinkStatusGet(cpswinst->mdio_base,
                           cpswinst->port[port_num - 1].phy_addr, 1000);

      if (1 == frc_stat) {
        break;
      }
      frc_stat_cnt--;
    }

    if (0 != frc_stat_cnt) {
      linkstat = ERR_OK;
      LWIP_PRINTF("\n\rPhy Configuration Successful.");
      LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.",
                  port_num, 0);
    } else {
      LWIP_PRINTF("\n\rPhy Configuration Successful.");
      LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.",
                  port_num, 0);
      return ERR_CONN;
    }

    if (SELECT_SPEED_1000 == speed) {
      LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps.");
      transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX;
    } else {
      if (SELECT_SPEED_10 == speed) {
        LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps ");
        transfer_mode = CPSW_SLIVER_INBAND;
      } else {
        LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps ");
      }
      if (TRUE == duplex) {
        LWIP_PRINTF("Full Duplex.");
        transfer_mode |= CPSW_SLIVER_NON_GIG_FULL_DUPLEX;
      } else {
        LWIP_PRINTF("Half Duplex.");
        transfer_mode |= CPSW_SLIVER_NON_GIG_HALF_DUPLEX;
      }
    }
  } else {
    LWIP_PRINTF("\n\rPhy Configuration Not Successful.");
    LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.",
                port_num, 0);
    linkstat = ERR_CONN;
  }

  /**
   * Set the Sliver with the forced phy configuration
   */
  CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base,
                        transfer_mode);

  CPSWSlRGMIIEnable(cpswinst->port[port_num - 1].sliver_base);

  return linkstat;
}

/**
* Function to setup the link. AutoNegotiates with the phy for link
* setup and set the CPSW with the result of autonegotiation.
* @param  cpswportif  The cpsw port interface structure pointer
* @return ERR_OK      If link set up is successful
*                     others if not successful
*/
static err_t
cpswif_autoneg_config(u32_t inst_num, u32_t port_num) {
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];
  err_t linkstat = ERR_CONN;
  u16_t adv_val, partnr_ablty, gbps_partnr_ablty, gig_adv_val;
  u32_t aut_neg_cnt = 200, auto_stat, transfer_mode = 0;

  /* We advertise for 10/100 Mbps both half and full duplex */
  adv_val = (PHY_100BTX | PHY_100BTX_FD | PHY_10BT | PHY_10BT_FD);

  /**
   * Not all the PHYs can operate at 1000 Mbps. So advertise only
   * if the PHY is capable
   */
  if(TRUE == cpswinst->port[port_num -1].phy_gbps) {
    gig_adv_val = PHY_1000BT_FD;
    partnr_ablty = TRUE;
    gbps_partnr_ablty = TRUE;
  } else {
    gig_adv_val = 0;
    partnr_ablty = TRUE;
    gbps_partnr_ablty = FALSE;
  }

  LWIP_PRINTF("\n\rPerforming Auto-Negotiation...");

  /**
   * Now start Autonegotiation. PHY will talk to its partner
   * and give us what the partner can handle
   */
  if(PhyAutoNegotiate(cpswinst->mdio_base,
                      cpswinst->port[port_num -1].phy_addr,
                      &adv_val, &gig_adv_val) == TRUE) {
    while(aut_neg_cnt) {
      delay(50);
      auto_stat = PhyAutoNegStatusGet(cpswinst->mdio_base,
                                      cpswinst->port[port_num -1].phy_addr);
      if(TRUE == auto_stat) {
        break;
      }
      aut_neg_cnt--;
    }

    if(0 != aut_neg_cnt) {
      linkstat = ERR_OK;
      LWIP_PRINTF("\n\rAuto-Negotiation Successful.");
    } else {
      LWIP_PRINTF("\n\rAuto-Negotiation Not Successful.");
      return ERR_CONN;
    }

    /* Get what the partner supports */
    PhyPartnerAbilityGet(cpswinst->mdio_base,
                         cpswinst->port[port_num -1].phy_addr,
                         &partnr_ablty, &gbps_partnr_ablty);
    if(gbps_partnr_ablty & PHY_LINK_PARTNER_1000BT_FD) {
      LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps.");
      transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX;
    } else {
      if ((adv_val & partnr_ablty) & PHY_100BTX_FD) {
        LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Full Duplex.");
        transfer_mode = CPSW_SLIVER_NON_GIG_FULL_DUPLEX;
      } else if ((adv_val & partnr_ablty) & PHY_100BTX) {
        LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Half Duplex.");
        transfer_mode = CPSW_SLIVER_NON_GIG_HALF_DUPLEX;
      } else if ((adv_val & partnr_ablty) & PHY_10BT_FD) {
        LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Full Duplex.");
        transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_FULL_DUPLEX;
      } else if ((adv_val & partnr_ablty) & PHY_10BT) {
        LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Half Duplex.");
        transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_HALF_DUPLEX;
      } else {
        LWIP_PRINTF("\n\rNo Valid Transfer Mode is detected.");
      }
    }
  } else {
    LWIP_PRINTF("\n\rAuto-Negotiation Not Successful.");
    linkstat = ERR_CONN;
  }

  /**
   * Set the Sliver with the negotiation results if autonegotiation
   * is successful
   */
  if(linkstat == ERR_OK) {
    CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base,
                          transfer_mode);
  }

  return linkstat;
}

/**
 * This function allocates the rx buffer descriptors ring. The function
 * internally calls pbuf_alloc() and allocates the pbufs to the rx buffer
 * descriptors.
 *
 * @param   cpswinst   The CPSW instance structure pointer
 * @return  None
 */
static void
cpswif_rxbd_alloc(struct cpswinst *cpswinst) {
  struct rxch *rxch = &(cpswinst->rxch);
  struct pbuf *p;
  volatile struct cpdma_rx_bd *curr_bd, *last_bd, *recv_tail, *recv_head;
  u32_t saved_free_num;

  /* Start from the free head of the chain */
  curr_bd = rxch->free_head;

  /* Note down the current positions */
  recv_head = rxch->free_head;
  recv_tail = rxch->recv_tail;
  saved_free_num = rxch->free_num;
  last_bd = rxch->recv_tail;

  while(rxch->free_num) {
    /**
     * Try to get a pbuf of max. length. This shall be cache line aligned if
     * cache is enabled.
     */
    p = pbuf_alloc(PBUF_RAW, PBUF_LEN_MAX, PBUF_POOL);

    /**
     * Allocate bd's if p is not NULL. This allocation doesnt support
     * pbuf chaining.
     */
    if(p != NULL) {
#ifdef LWIP_CACHE_ENABLED
      /**
       * Clean the pbuf structure info. This is needed to prevent losing
       * pbuf structure info when we invalidate the pbuf on rx interrupt
       */
      CacheDataCleanBuff((u32_t)(p), (u32_t)(SIZEOF_STRUCT_PBUF));
#endif
      curr_bd->bufptr = (u32_t)(p->payload);
      curr_bd->bufoff_len = p->len;
      curr_bd->flags_pktlen = CPDMA_BUF_DESC_OWNER;

      /* Save the pbuf */
      curr_bd->pbuf = p;
      last_bd = curr_bd;
      curr_bd = curr_bd->next;
      rxch->free_num--;
    } else {
      break;
    }
  }

  if(saved_free_num == rxch->free_num) {
    /* No bd's were allocated. Go back. */
    return;
  }

  rxch->recv_tail = last_bd;

  /**
   * If the entire ring is traversed, curr_bd will be NULL. In that case,
   * write the Rx HDP in order to continue reception
   */
  if(NULL != curr_bd) {
    rxch->free_head = curr_bd;
  } else {
    CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(recv_head), 0);
  }

  recv_tail->next = recv_head;

  /* Close the ring to prevent overwriting of pbuf data */
  last_bd->next = NULL;

  /**
   * Check if the reception has ended. If the EOQ flag is set, the NULL
   * Pointer is  already taken by the DMA engine. So we need to write the
   * RX HDP with the next descriptor.
   */
  if(recv_tail->flags_pktlen & CPDMA_BUF_DESC_EOQ) {
    CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(recv_head), 0);
  }
}

/**
 * This function does the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf might be
 * chained. That is, one pbuf can span more than one tx buffer descriptors
 *
 * @param netif    the network interface state for this ethernetif
 * @param pbuf     the pbuf which is to be sent over EMAC
 * @return status  ERR_OK, if transmit was successful
 *                 ERR_MEM, if no memory available
 */
static err_t
cpswif_transmit(struct netif *netif, struct pbuf *pbuf) {
  struct pbuf *q;
  struct txch *txch;
  volatile struct cpdma_tx_bd *curr_bd, *bd_to_send, *bd_end;
  struct cpswportif *cpswif = netif->state;
  u32_t inst_num = cpswif->inst_num;
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];

#ifdef CPSW_DUAL_MAC_MODE
  u32_t port_num = cpswif->port_num;
#endif

  txch = &(cpswinst->txch);

  /* Do not send if there are no enough free bd's */
  if(pbuf_clen(pbuf) > txch->free_num) {
    return ERR_MEM;
  }

  /* Get the buffer descriptor which is free to transmit */
  curr_bd = txch->free_head;

  bd_to_send = txch->free_head;

  /* Update the total packet length */
  curr_bd->flags_pktlen = pbuf->tot_len;

  /* Indicate the start of the packet */
  curr_bd->flags_pktlen |= (CPDMA_BUF_DESC_SOP | CPDMA_BUF_DESC_OWNER);

#ifdef CPSW_DUAL_MAC_MODE
  /* Indicate to which port the packet has to be sent */
  curr_bd->flags_pktlen |= CPDMA_BUF_DESC_TO_PORT(port_num);
#endif

  /* Copy pbuf information into TX buffer descriptors */
  for(q = pbuf; q != NULL; q = q->next) {
#ifdef LWIP_CACHE_ENABLED
    /**
     * Make sure that the payload is written to memory. Clean
     * the portion of cache to make it coherent with the memory.
     */
    CacheDataCleanBuff((u32_t)(q->payload), (u32_t)(q->len));
#endif
    /* Intialize the buffer pointer and length */
    curr_bd->bufptr = (u32_t)(q->payload);
    curr_bd->bufoff_len = (q->len) & CPDMA_BD_LEN_MASK;
    bd_end = curr_bd;
    curr_bd->pbuf = pbuf;
    curr_bd = curr_bd->next;

    /* Decrement free bds, since one is consumed */
    txch->free_num--;
  }
  /* Indicate the end of the packet */
  bd_end->next = NULL;
  bd_end->flags_pktlen |= CPDMA_BUF_DESC_EOP;
  bd_end->flags_pktlen &= ~CPDMA_BUF_DESC_EOQ;

  txch->free_head = curr_bd;

  /* For the first time, write the HDP with the filled bd */
  if(txch->send_tail == NULL) {
    CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base,
                               (u32_t)(bd_to_send), 0);
  } else {
    /**
     * Chain the bd's. If the DMA engine, already reached the end of the chain,
     * the EOQ will be set. In that case, the HDP shall be written again.
     */
    curr_bd = txch->send_tail;
    curr_bd->next = bd_to_send;

    if(curr_bd->flags_pktlen & CPDMA_BUF_DESC_EOQ) {
      /* Write the Header Descriptor Pointer and start DMA */
      CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base,
                                 (u32_t)(bd_to_send), 0);
    }
  }

  txch->send_tail = bd_end;

  return ERR_OK;
}

/**
 * This function will send a packet through the emac if the channel is
 * available. Otherwise, the packet will be queued in a pbuf queue.
 *
 * @param netif   The lwip network interface structure for this ethernetif
 * @param p       The MAC packet to send (e.g. IP packet including
 *                MAC addresses and type)
 * @return        ERR_OK if the packet could be sent
 *                an err_t value if the packet couldn't be sent
 *
 */
static err_t
cpswif_output(struct netif *netif, struct pbuf *p) {
  err_t stat;
  struct pbuf *q;
  struct cpswportif *cpswif = netif->state;
  u32_t inst_num = cpswif->inst_num;
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];
  SYS_ARCH_DECL_PROTECT(lev);

  /**
   * This entire function must run within a "critical section" to preserve
   * the integrity of the transmit pbuf queue.
   */

  sys_mutex_lock(&cpswinst->txmtx);
  SYS_ARCH_PROTECT(lev);

  /**
   * Adjust the packet length if less than minimum required.
   */
  if(p->tot_len < MIN_PKT_LEN) {
      p->tot_len = MIN_PKT_LEN;

      for(q = p; q->next != NULL; q = q->next) {
        q->next->tot_len = q->tot_len - q->len;
      }

    /* Adjust the length of the last pbuf. (contents - don't care) */
    q->len = q->tot_len;
  }

  /**
   * Bump the reference count on the pbuf to prevent it from being
   * freed till we are done with it.
   */
  pbuf_ref(p);

  /* call the actual transmit function */
  stat = cpswif_transmit(netif, p);

  /* Return to prior interrupt state and return. */
  SYS_ARCH_UNPROTECT(lev);
  sys_mutex_unlock(&cpswinst->txmtx);

  return stat;
}

/**
 * Configures PHY link for a port
 * @param cpswif  The CPSW interface structure pointer
 * @param slv_port_num  The slave port number
 *
 * @return ERR_OK    if link configurations are successful
 *                   an error status if failed
 */
static err_t
cpswif_phylink_config(struct cpswportif * cpswif, u32_t slv_port_num) {
  struct cpswinst *cpswinst = &cpsw_inst_data[cpswif->inst_num];
  err_t err;

  /* Check if ethernet PHY is present or not */
  if(0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base)
        & (1 << cpswinst->port[slv_port_num - 1].phy_addr))){
    LWIP_PRINTF("\n\rNo PHY found at address %d for  Port %d of Instance %d.",
                cpswinst->port[slv_port_num - 1].phy_addr, slv_port_num,
                cpswif->inst_num);
    return ERR_CONN;
  }

  LWIP_PRINTF("\n\rPHY found at address %d for  Port %d of Instance %d.",
              cpswinst->port[slv_port_num - 1].phy_addr, slv_port_num,
              cpswif->inst_num);

  /**
   * PHY is alive. So autonegotiate and get the speed and duplex
   * parameters, set it in the sliver
   */
  err = (err_t)(cpswif_autoneg_config(cpswif->inst_num, slv_port_num));

  /* Check if PHY link is there or not */
  if(FALSE == ((PhyLinkStatusGet(cpswinst->mdio_base,
                           cpswinst->port[slv_port_num - 1].phy_addr, 1000)))) {
    LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Instance %d.",
                slv_port_num, cpswif->inst_num);
    return ERR_CONN;
  }

  LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.",
              slv_port_num, cpswif->inst_num);

  CPSWSlRGMIIEnable(cpswinst->port[slv_port_num - 1].sliver_base);

  return err;
}

/**
 * Initializes the CPSW port
 * @param netif   The cpsw interface
 *
 * @return ERR_OK    if port initialization is successful
 *                   an error status if failed
 */
static err_t
cpswif_port_init(struct netif *netif) {
  struct cpswportif *cpswif = (struct cpswportif*)(netif->state);
  u32_t temp;
  err_t err;

#ifdef CPSW_DUAL_MAC_MODE
  struct cpswinst *cpswinst = &cpsw_inst_data[cpswif->inst_num];
  u32_t curr_port = cpswif->port_num;
#endif
  sem_init(&cpswinst->rxsem, SEM_DEFAULT_PSHARED, SEM_INITIAL_VALUE);
  
  cpswinst->RxThread = sys_thread_new("receive_thread", rx_thread_function, netif, RX_THREAD_STACKSIZE, RX_PRIORITY);
  LWIP_ASSERT("RxThread creation error", (cpswinst->RxThread));

  sem_init(&cpswinst->txsem, SEM_DEFAULT_PSHARED, SEM_INITIAL_VALUE);

  cpswinst->TxThread = sys_thread_new("transmit_thread", tx_thread_function, netif, TX_THREAD_STACKSIZE, TX_PRIORITY);
  LWIP_ASSERT("TxThread creation error", (cpswinst->TxThread));

  err = sys_mutex_new(&cpswinst->txmtx);
  LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK));

  /* set MAC hardware address length */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

  /* set MAC hardware address */
  for(temp = 0; temp < ETHARP_HWADDR_LEN; temp++) {
    netif->hwaddr[temp] = cpswif->eth_addr[temp];
  }

  /* maximum transfer unit */
  netif->mtu = MAX_TRANSFER_UNIT;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

#ifdef CPSW_DUAL_MAC_MODE
  /* Set the ethernet address for the port */
  CPSWPortSrcAddrSet(cpswinst->port[curr_port - 1].port_base,
                    (u8_t *)(&(cpswif->eth_addr)));

  /**
   * For Dual Mac mode, configure port0 and port1 for one VLAN ID;
   * port0 and port2 for a different VLAN ID. Here we choose the
   * port number as VLAN ID.
   */
  CPSWPortVLANConfig(cpswinst->port[curr_port - 1].port_base, curr_port, 0, 0);

  cpswif_port_to_host_vlan_cfg(cpswinst, curr_port,
                               (u8_t *)(&(cpswif->eth_addr)));

  err = cpswif_phylink_config(cpswif, curr_port);

#else
  err = cpswif_phylink_config(cpswif, 1);
  err = err & (cpswif_phylink_config(cpswif, 2));

#endif

  return err;
}

/**
 * This function intializes the CPDMA.
 * The CPPI RAM will be initialized for transmit and receive
 * buffer descriptor rings.
 *
 * @param cpswinst   The CPSW instance structure pointer
 * @return None
 */
static void
cpswif_cpdma_init(struct cpswinst *cpswinst) {
  u32_t num_bd;
  volatile struct cpdma_tx_bd *curr_txbd, *last_txbd;
  volatile struct cpdma_rx_bd *curr_rxbd, *last_rxbd;
  struct txch *txch;
  struct rxch *rxch;

  txch = &(cpswinst->txch);

  /* Initialize the CPDMA memory. Only Channel 0 is supported */
  txch->free_head = (volatile struct cpdma_tx_bd*)(cpswinst->cppi_ram_base);
  txch->send_head = txch->free_head;
  txch->send_tail = NULL;

  /* Allocate half of the CPPI RAM for TX buffer descriptors */
  num_bd = (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_tx_bd);

  /* All buffer descriptors are free to send */
  txch->free_num = num_bd;

  curr_txbd = txch->free_head;

  /* Initialize all the TX buffer descriptors ring */
  while(num_bd) {
    curr_txbd->next = curr_txbd + 1;
    curr_txbd->flags_pktlen = 0;
    last_txbd = curr_txbd;
    curr_txbd = curr_txbd->next;
    num_bd--;
  }
  last_txbd->next = txch->free_head;

  /* Initialize the descriptors for the RX channel */
  rxch = &(cpswinst->rxch);
  rxch->free_head = (volatile struct cpdma_rx_bd*)(cpswinst->cppi_ram_base +
                                                   (SIZE_CPPI_RAM >> 1));

  /* Allocate half of the CPPI RAM available for RX buffer dscriptors */
  num_bd = (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_rx_bd);
  rxch->free_num = num_bd;

  curr_rxbd = rxch->free_head;

  /* Create the rx ring of buffer descriptors */
  while(num_bd) {
    curr_rxbd->next = curr_rxbd + 1;
    curr_rxbd->flags_pktlen = CPDMA_BUF_DESC_OWNER;
    last_rxbd = curr_rxbd;
    curr_rxbd = curr_rxbd->next;
    num_bd--;
  }

  last_rxbd->next = rxch->free_head;

  /* We are going to receive starting from the free head */
  rxch->recv_head = rxch->free_head;
  rxch->recv_tail = last_rxbd;
  cpswif_rxbd_alloc(cpswinst);

  /* close the ring */
  last_rxbd->next = NULL;

  CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(rxch->recv_head), 0);
}

/**
 * In this function, the hardware should be initialized.
 * Called from cpswif_init().
 *
 * @param cpswportif  The CPSW port interface structure pointer
 * @return None
 */
static void
cpswif_inst_init(struct cpswportif *cpswif){
  u32_t inst_num = cpswif->inst_num;
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];

  /* Reset the different modules */
  CPSWSSReset(cpswinst->ss_base);
  CPSWWrReset(cpswinst->wrpr_base);
  CPSWSlReset(cpswinst->port[PORT_1].sliver_base);
  CPSWSlReset(cpswinst->port[PORT_2].sliver_base);
  CPSWCPDMAReset(cpswinst->cpdma_base);

  /* Initialize MDIO */
  MDIOInit(cpswinst->mdio_base, MDIO_FREQ_INPUT, MDIO_FREQ_OUTPUT);
  delay(1);

  CPSWALEInit(cpswinst->ale_base);

  /* Set the port 0, 1 and 2 states to FORWARD */
  CPSWALEPortStateSet(cpswinst->ale_base, 0, CPSW_ALE_PORT_STATE_FWD);
  CPSWALEPortStateSet(cpswinst->ale_base, 1, CPSW_ALE_PORT_STATE_FWD);
  CPSWALEPortStateSet(cpswinst->ale_base, 2, CPSW_ALE_PORT_STATE_FWD);

#ifdef CPSW_DUAL_MAC_MODE
  /* For Dual Mac Mode, Configure for VLAN Aware Mode */
  CPSWALEVLANAwareSet(cpswinst->ale_base);
  CPSWHostPortDualMacModeSet(cpswinst->host_port_base);

#else  /*CPSW_DUAL_MAC_MODE */
  /* For normal CPSW switch mode, set multicast entry. */
  u8_t bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
  cpswif_ale_multicastentry_set(cpswinst,
                                PORT_0_MASK | PORT_1_MASK | PORT_2_MASK,
                                bcast_addr);
  cpswif_ale_unicastentry_set(cpswinst, 0,
                              (u8_t *)(&(cpswif->eth_addr)));

  /* Set the ethernet address for both the ports */
  CPSWPortSrcAddrSet(cpswinst->port[0].port_base,
                    (u8_t *)(&(cpswif->eth_addr)));
  CPSWPortSrcAddrSet(cpswinst->port[1].port_base,
                    (u8_t *)(&(cpswif->eth_addr)));

#endif /*CPSW_DUAL_MAC_MODE */

  /* Enable the statistics. Lets see in case we come across any issues */
  CPSWStatisticsEnable(cpswinst->ss_base);

  /* Initialize the buffer descriptors for CPDMA */
  cpswif_cpdma_init(cpswinst);

  /* Acknowledge receive and transmit interrupts for proper interrupt pulsing*/
  CPSWCPDMAEndOfIntVectorWrite(cpswinst->cpdma_base, CPSW_EOI_TX_PULSE);
  CPSWCPDMAEndOfIntVectorWrite(cpswinst->cpdma_base, CPSW_EOI_RX_PULSE);

  /* Enable the transmission and reception */
  CPSWCPDMATxEnable(cpswinst->cpdma_base);
  CPSWCPDMARxEnable(cpswinst->cpdma_base);

  /* Enable the interrupts for channel 0 and for control core 0 */
  CPSWCPDMATxIntEnable(cpswinst->cpdma_base, 0);
  CPSWWrCoreIntEnable(cpswinst->wrpr_base, 0, 0, CPSW_CORE_INT_TX_PULSE);

  CPSWCPDMARxIntEnable(cpswinst->cpdma_base, 0);
  CPSWWrCoreIntEnable(cpswinst->wrpr_base, 0, 0, CPSW_CORE_INT_RX_PULSE);
}

/**
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the functions cpswif_inst_init() and
 * cpswif_port_init() to do low level initializations
 *
 * @param netif     The lwip network interface structure for this ethernetif
 * @return ERR_OK   If the interface is initialized
 *                  any other err_t on error
 */
err_t
cpswif_init(struct netif *netif)
{
  struct cpswportif *cpswif = (struct cpswportif*)(netif->state);
  static u32_t inst_init_flag = 0;
  u32_t inst_num = cpswif->inst_num;

#if LWIP_NETIF_HOSTNAME
  /* Initialize interface hostname */
  netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */

  /**
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 10000000);

  /* let us use the interface number to identify netif */
#ifdef CPSW_DUAL_MAC_MODE
  netif->num = (u8_t)(((cpswif->inst_num * MAX_SLAVEPORT_PER_INST)
                      + cpswif->port_num - 1) & 0xFF);
#else
  netif->num = (u8_t)(cpswif->inst_num);
#endif

  /**
   * We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...)
   */
  netif->output = etharp_output;
  netif->linkoutput = cpswif_output;

  /**
   * Initialize an instance only once. Port initialization will be
   * done separately.
   */
  if(((inst_init_flag >> inst_num) & 0x01) == 0) {
    cpswif_inst_config(cpswif);
    cpswif_inst_init(cpswif);
    inst_init_flag |= (1 << inst_num);
  }

  if(cpswif_port_init(netif) != ERR_OK) {
    return ERR_CONN;
  }

  return ERR_OK;
}

/**
 * Handler for Receive interrupt. Packet processing is done in this
 * interrupt handler itself.
 *
 * @param inst_num   the instance for which interrupt was generated
 * @param netif_arr  the address of the array of netifs
 * @return none
 */
void
cpswif_rx_inthandler(const u32_t inst_num) {
  struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num];
  sem_t* const rxsem = &cpswinst->rxsem;
  const u32_t cpdma_base = cpswinst->cpdma_base;
  unsigned int curr_bd;

  sem_post(rxsem);

  /* Get the bd which contains the earliest filled data */
  curr_bd = CPSWCPDMARxCPRead(cpdma_base, 0);
  /* Acknowledge that this packet is processed */
  CPSWCPDMARxCPWrite(cpdma_base, 0, (unsigned int)curr_bd);
  CPSWCPDMAEndOfIntVectorWrite(cpdma_base, CPSW_EOI_RX_PULSE);
}

static void
process_input(const u32_t inst_num, struct netif *const netif_arr) {
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];
  struct rxch *rxch;
  volatile struct cpdma_rx_bd *curr_bd;
  volatile struct pbuf *pbuf;
  u32_t tot_len, if_num;

#ifdef CPSW_DUAL_MAC_MODE
  u32_t from_port;
#endif

  /* Get the rx channel pointer */
  rxch = &(cpswinst->rxch);

  /* Get the bd which contains the earliest filled data */
  curr_bd = rxch->recv_head;

  /**
   * Process the receive buffer descriptors. When the DMA completes
   * reception, OWNERSHIP flag will be cleared.
   */
  while((curr_bd->flags_pktlen & CPDMA_BUF_DESC_OWNER)
           != CPDMA_BUF_DESC_OWNER) {

#ifdef CPSW_DUAL_MAC_MODE
    /**
     * From which slave port the packet came from ?
     * We will use this to decide to which netif the packet
     * is to be forwarded to.
     */
    from_port = ((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_FROM_PORT)
                 >> CPDMA_BUF_DESC_FROM_PORT_SHIFT;
#endif

    /* Get the total length of the packet */
    tot_len = (curr_bd->flags_pktlen) & CPDMA_BD_PKTLEN_MASK;

    /* Get the pbuf which is associated with the current bd */
    pbuf = curr_bd->pbuf;
#ifdef LWIP_CACHE_ENABLED
    /**
     * Invalidate the cache lines of the pbuf including payload. Because
     * the memory contents got changed by DMA.
     */
    CacheDataInvalidateBuff((u32_t)pbuf, (PBUF_LEN_MAX + SIZEOF_STRUCT_PBUF));
#endif

    /* Update the len and tot_len fields for the pbuf in the chain */
    pbuf->len = (u16_t)(tot_len);
    pbuf->tot_len = (u16_t)(tot_len);

    curr_bd->flags_pktlen = CPDMA_BUF_DESC_OWNER;

    /* Adjust the link statistics */
    LINK_STATS_INC(link.recv);

#ifdef CPSW_DUAL_MAC_MODE
    if_num = (inst_num * MAX_SLAVEPORT_PER_INST) + from_port - 1;
#else
    if_num = inst_num;
#endif
    struct netif * netif = netif_arr + if_num;
    /* Process the packet */
    if(netif->input((struct pbuf *)pbuf, netif) != ERR_OK) {
      /* Adjust the link statistics */
      LINK_STATS_INC(link.memerr);
      LINK_STATS_INC(link.drop);
    }

    curr_bd = curr_bd->next;

    /* One more buffer descriptor is free now */
    rxch->free_num++;

    /**
     * If the DMA engine took the NULL pointer, we dont have any bd to
     * process until new bd's are allocated.
     */
    if(curr_bd == NULL) {
      rxch->recv_head = rxch->free_head;
      break;
    }
    rxch->recv_head = curr_bd;
  }

  /* We got some bd's freed; Allocate them */
  cpswif_rxbd_alloc(cpswinst);
}

static void
rx_thread_function(void* arg) {
struct netif *const netif = arg;
struct cpswportif * const cpswif = netif->state;
const u32_t inst_num = cpswif->inst_num;
struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num];
sem_t *const rxsem = &cpswinst->rxsem;
while (1) {
        sem_wait(rxsem);
        process_input(inst_num, netif);
	sched_yield();
    }
}

/**
 * Handler for CPSW Transmit interrupt
 *
 * @param netif the lwip network interface structure for this ethernetif
 * @return none
 */
void
cpswif_tx_inthandler(const u32_t inst_num) {
  struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num];
  sem_t* const txsem = &cpswinst->txsem;
  const u32_t cpdma_base = cpswinst->cpdma_base;
  unsigned int curr_bd;

  sem_post(txsem);

  /* Get the bd which contains the earliest filled data */
  curr_bd = CPSWCPDMATxCPRead(cpdma_base, 0);
  /* Acknowledge that this packet is processed */
  CPSWCPDMATxCPWrite(cpdma_base, 0, (unsigned int)curr_bd);
  CPSWCPDMAEndOfIntVectorWrite(cpdma_base, CPSW_EOI_TX_PULSE);
}

static void process_tx_end(const u32_t inst_num)
{
  struct txch *txch;
  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];
  volatile struct cpdma_tx_bd *curr_bd, *send_head;
  volatile u32_t cnt = 0xFFFF;

  txch = &(cpswinst->txch);

  send_head = txch->send_head;

  curr_bd = send_head;

  /* Check for correct start of packet */
  while((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_SOP) {

    /* Make sure that the transmission is over */
    while(((curr_bd->flags_pktlen & CPDMA_BUF_DESC_OWNER)
          == CPDMA_BUF_DESC_OWNER) && ((--cnt) != 0));

    /* If CPDMA failed to transmit, give it a chance once more */
    if(0 == cnt) {
      CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base,
                                 (u32_t)(curr_bd), 0);
      return;
    }

    /* One buffer descriptor is free now */
    txch->free_num++;

    /* Traverse till the end of packet is reached */
    while(((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_EOP) != CPDMA_BUF_DESC_EOP) {
      curr_bd = curr_bd->next;

      /* As this bd is not the end, its free now */
      txch->free_num++;

      if(txch->free_num == (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_tx_bd)) {
        break;
      }
    }

    send_head->flags_pktlen &= ~(CPDMA_BUF_DESC_SOP);
    curr_bd->flags_pktlen &= ~(CPDMA_BUF_DESC_EOP);

    /**
     * If there are no more data transmitted, the next interrupt
     * shall happen with the pbuf associated with the free_head
     */
    if(curr_bd->next == NULL) {
      txch->send_head = txch->free_head;
    } else {
      txch->send_head = curr_bd->next;
    }

    pbuf_free((struct pbuf *)curr_bd->pbuf);

    LINK_STATS_INC(link.xmit);

    send_head = txch->send_head;
    curr_bd = send_head;
  }
}

static void
tx_thread_function(void* arg) {
struct netif * const netif = arg;
struct cpswportif * const cpswif = netif->state;
const u32_t inst_num = cpswif->inst_num;
struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num];
sem_t* const txsem = &cpswinst->txsem;
sys_mutex_t* const txmtx = &cpswinst->txmtx;
while (1) {
        /* Wait for receive task to wakeup */
        sem_wait(txsem);
        sys_mutex_lock(txmtx);
        process_tx_end(inst_num);
        sys_mutex_unlock(txmtx);
	sched_yield();
    }
}

/**
 * Gets the netif status
 *
 * @param   netif   The netif whoes status to be checked
 * @return  The netif status
 */
u32_t
cpswif_netif_status(struct netif *netif) {
  return ((u32_t)(netif_is_up(netif)));
}

/**
 * Returns the link status
 *
 * @param   inst_num      The instance number of the module
 * @param   slv_port_num  The slave port number for the module
 *
 * @return  the link status
 */
u32_t
cpswif_link_status(u32_t inst_num, u32_t slv_port_num) {

  struct cpswinst *cpswinst = &cpsw_inst_data[inst_num];

  return (PhyLinkStatusGet(cpswinst->mdio_base,
                           cpswinst->port[slv_port_num - 1].phy_addr, 3));
}

/**
 * Checks the value is in the range of min and max
 *
 * @param   vlaue      Value
 * @param   min        Minimum Value
 * @param   max        Maximum Value
 *
 * @return  the status
 */
static u32_t
check_valid(u32_t value, u32_t min, u32_t max) {
  if ((min <= value) && (value <= max))
    return TRUE;
  else
    return FALSE;
}

/*
 * Executes following CPSW Configutarions
 * Switch Configuration (CPSW_SWITCH_CONFIG has to be defined)
 *  1 - Add a multicast entry
 *  2 - Add a unicast entry
 *  3 - Add a OUI entry
 *  4 - Search address in entry list
 *  5 - Delete a multicast entry
 *  6 - Delete a unicast entry
 *  7 - Adds a vlan entry
 *  8 - Search vlan in entry list
 *  9 - Delete vlan
 * 10 - Configure Port Vlan (ID, CFI, PRI)
 * 11 - Age Out the Untouched entries of ALE Table
 * 12 - Print Dump of Switch
 * 13 - Print Dump of Switch Config
 * 14 - ALE VLAN Aware Config
 * 15 - Configure Rate Limit for TX or RX
 * 16 - Enable Engress Check
 * 17 - Set port unknown VLAN info
 * 18 - Enable MAC Auth
 * 19 - Configure Port State
 * Phy Configuration
 *  1 - Configure PHY of a port
 *
 * @param   cpsw_switch_config  parameters required for configuration
 *
 * @return  None
*/
void
cpsw_switch_configuration(struct cpsw_config *cpsw_config) {
  struct cpswinst *cpswinst = &cpsw_inst_data[cpsw_config->cpsw_inst];
  struct cpsw_phy_param *cpsw_phy_param = cpsw_config->phy_param;
#ifdef CPSW_SWITCH_CONFIG
  struct cpsw_switch_param *cpsw_switch_param = cpsw_config->switch_param;
  s32_t ret;
#endif /* CPSW_SWITCH_CONFIG */

  switch (cpsw_config->cmd) {
#ifdef CPSW_SWITCH_CONFIG
    case CONFIG_SWITCH_ADD_MULTICAST:
    {
      if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_MULTICAST)) {
        cpsw_config->ret = ERR_ADDR;
        break;
      }

      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK,
                      MAX_PORT_MASK)) {
        cpsw_config->ret = ERR_PORT_MASK;
        break;
      }

      if (!check_valid(cpsw_switch_param->super, MIN_SUPER, MAX_SUPER)) {
        cpsw_config->ret = ERR_SUPER;
        break;
      }

      if (!check_valid(cpsw_switch_param->fwd_state, MIN_MCAST_FWD_STATE,
                      MAX_MCAST_FWD_STATE)) {
        cpsw_config->ret = ERR_MCAST_FWD_STATE;
        break;
      }

      if (cpsw_switch_param->vid == 0)
        ret = cpswif_ale_multicastentry_add(cpswinst,
                                            cpsw_switch_param->port_mask,
                                            cpsw_switch_param->addr,
                                            cpsw_switch_param->super,
                                            cpsw_switch_param->fwd_state);
      else
        ret = cpswif_ale_vlan_add_mcast(cpswinst, cpsw_switch_param->vid,
                                        cpsw_switch_param->port_mask,
                                        cpsw_switch_param->addr,
                                        cpsw_switch_param->super,
                                        cpsw_switch_param->fwd_state);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_ADD_UNICAST:
    {
      if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) {
        cpsw_config->ret = ERR_ADDR;
        break;
      }

      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      if (!check_valid(cpsw_switch_param->ucast_flags, MIN_UCAST_FLAGS,
                      MAX_UCAST_FLAGS)) {
        cpsw_config->ret = ERR_UCAST_FLAGS;
        break;
      }

      if (!check_valid(cpsw_switch_param->ucast_type, MIN_UCAST_FLAGS,
                      MAX_UCAST_FLAGS)) {
        cpsw_config->ret = ERR_UCAST_FLAGS;
        break;
      }

      if (cpsw_switch_param->vid == 0)
        ret = cpswif_ale_unicastentry_add(cpswinst, cpsw_switch_param->port_num,
                                          cpsw_switch_param->addr,
                                          cpsw_switch_param->ucast_flags,
                                          cpsw_switch_param->ucast_type);
      else
        ret = cpswif_ale_vlan_add_ucast(cpswinst, cpsw_switch_param->vid,
                                        cpsw_switch_param->port_num,
                                        cpsw_switch_param->addr,
                                        cpsw_switch_param->ucast_flags,
                                        cpsw_switch_param->ucast_type);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_ADD_OUI:
    {
      if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) {
        cpsw_config->ret = ERR_ADDR;
        break;
      }

      ret = cpswif_ale_OUI_add(cpswinst, cpsw_switch_param->addr);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_FIND_ADDR:
    {
      if (!check_valid_addr(cpsw_switch_param->addr, 0)) {
        cpsw_config->ret = ERR_ADDR;
        break;
      }

      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      ret = cpswif_ale_entry_match_addr(cpswinst, cpsw_switch_param->addr,
                                        cpsw_switch_param->vid);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_DEL_MULTICAST:
    {
      if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_MULTICAST)) {
        cpsw_config->ret = ERR_ADDR;
        break;
      }

      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK,
                      MAX_PORT_MASK)) {
        cpsw_config->ret = ERR_PORT_MASK;
        break;
      }

      if (cpsw_switch_param->vid == 0)
        ret = cpswif_ale_multicastentry_del(cpswinst, cpsw_switch_param->port_mask,
                                            cpsw_switch_param->addr);
      else
        ret = cpswif_ale_vlan_del_mcast(cpswinst, cpsw_switch_param->vid,
                                        cpsw_switch_param->port_mask,
                                        cpsw_switch_param->addr);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_DEL_UNICAST:
    {
      if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) {
        cpsw_config->ret = ERR_ADDR;
        break;
      }

      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      if (cpsw_switch_param->vid == 0)
        ret = cpswif_ale_unicastentry_del(cpswinst, cpsw_switch_param->port_num,
                                          cpsw_switch_param->addr);
      else
        ret = cpswif_ale_vlan_del_ucast(cpswinst, cpsw_switch_param->vid,
                                        cpsw_switch_param->port_num,
                                        cpsw_switch_param->addr);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_ADD_VLAN:
    {
      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      if (!check_valid(cpsw_switch_param->reg_multi, MIN_VLAN_MCAST_REG,
                      MAX_VLAN_MCAST_REG)) {
        cpsw_config->ret = ERR_VLAN_MCAST_REG;
        break;
      }

      if (!check_valid(cpsw_switch_param->unreg_multi, MIN_VLAN_MCAST_UNREG,
                      MAX_VLAN_MCAST_UNREG)) {
        cpsw_config->ret = ERR_VLAN_MCAST_UNREG;
        break;
      }

      if (!check_valid(cpsw_switch_param->untag_port, MIN_VLAN_UNTAG,
                      MAX_VLAN_UNTAG)) {
        cpsw_config->ret = ERR_VLAN_MCAST_UNTAG;
        break;
      }

      ret = cpswif_ale_vlan_add(cpswinst, cpsw_switch_param->vid,
                                cpsw_switch_param->port_num,
                                cpsw_switch_param->untag_port,
                                cpsw_switch_param->reg_multi,
                                cpsw_switch_param->unreg_multi);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_FIND_VLAN:
    {
      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      ret = cpswif_ale_entry_match_vlan(cpswinst, cpsw_switch_param->vid);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_DEL_VLAN:
    {
      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      ret = cpswif_ale_vlan_del(cpswinst, cpsw_switch_param->vid,
                                cpsw_switch_param->port_num);

      if (ret == ERR_VAL)
        cpsw_config->ret = ERR_FAIL;
      else
        cpsw_config->ret = ret;

      break;
    }

    case CONFIG_SWITCH_PORT_VLAN_CONFIG:
    {
      if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) {
        cpsw_config->ret = ERR_VLANID;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      if (!check_valid(cpsw_switch_param->cfi_port, MIN_CFI, MAX_CFI)) {
        cpsw_config->ret = ERR_CFI;
        break;
      }

      if (!check_valid(cpsw_switch_param->prio_port, MIN_PRI, MAX_PRI)) {
        cpsw_config->ret = ERR_PRI;
        break;
      }

      if (cpsw_switch_param->port_num == 0)
          CPSWPortVLANConfig(cpswinst->host_port_base, cpsw_switch_param->vid,
                             cpsw_switch_param->cfi_port,
                             cpsw_switch_param->prio_port);
      else if (cpsw_switch_param->port_num <= 2)
          CPSWPortVLANConfig(
                        cpswinst->port[cpsw_switch_param->port_num - 1].port_base,
                        cpsw_switch_param->vid,
                        cpsw_switch_param->cfi_port,
                        cpsw_switch_param->prio_port);

      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_AGEOUT:
    {
      CPSWALEAgeOut(cpswinst->ale_base);
      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_DUMP:
    {
      u32_t cnt = 0;

      for(cnt = 0; cnt <= MAX_ALE_ENTRIES; cnt++)
      {
        CPSWALETableEntryGet(cpswinst->ale_base, cnt,
                             cpsw_config->buf[cnt]);
      }

      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_CONFIG_DUMP:
    {

      if (!check_valid(cpsw_switch_param->ale_tbl_index, MIN_ALE_ENTRY_IDX,
                      MAX_ALE_ENTRY_IDX)) {
        cpsw_config->ret = ERR_ALE_ENTRY_IDX;
        break;
      }

      CPSWALETableEntryGet(cpswinst->ale_base, cpsw_switch_param->ale_tbl_index,
                           cpsw_config->ale_entry);
      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_VLANAWARE:
    {
      if (!check_valid(cpsw_switch_param->vlan_aware, MIN_VLANAWARE,
                      MAX_VLANAWARE)) {
        cpsw_config->ret = ERR_VLANAWARE;
        break;
      }

      if (!check_valid(cpsw_switch_param->drop_packet, MIN_ALE_VLANAWARE,
                      MAX_ALE_VLANAWARE)) {
        cpsw_config->ret = ERR_ALE_VLANAWARE;
        break;
      }

      if (!cpsw_switch_param->vlan_aware)
        CPSWVLANAwareDisable(cpswinst->ale_base);
      else
        CPSWVLANAwareEnable(cpswinst->ale_base);

      if (!cpsw_switch_param->drop_packet)
        CPSWALEVLANAwareClear(cpswinst->ale_base);
      else
        CPSWALEVLANAwareSet(cpswinst->ale_base);

      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_RATELIMIT:
    {
      if (!check_valid(cpsw_switch_param->enable, MIN_ENABLE, MAX_ENABLE)) {
        cpsw_config->ret = ERR_ENABLE;
        break;
      }

      if (!check_valid(cpsw_switch_param->direction, MIN_DIRECTION,
                       MAX_DIRECTION)) {
        cpsw_config->ret = ERR_DIRECTION;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      if (!check_valid(cpsw_switch_param->addr_type, MIN_ADDR_TYPE, MAX_ADDR_TYPE)) {
        cpsw_config->ret = ERR_ADDR_TYPE;
        break;
      }

      if (!check_valid(cpsw_switch_param->limit, MIN_LIMIT, MAX_LIMIT)) {
        cpsw_config->ret = ERR_LIMIT;
        break;
      }

      cpswif_rate_limit(cpswinst, cpsw_switch_param->enable,
                        cpsw_switch_param->direction,
                        cpsw_switch_param->port_num, cpsw_switch_param->addr_type,
                        cpsw_switch_param->limit);
      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_VID_INGRESS_CHECK:
    {
      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      CPSWALEVIDIngressCheckSet(cpswinst->ale_base, cpsw_switch_param->port_num);
      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_ADD_UNKNOWN_VLAN_INFO:
    {
      if (!check_valid(cpsw_switch_param->unknown_vlan, MIN_UNKNOWN_VLAN,
                      MAX_UNKNOWN_VLAN)) {
        cpsw_config->ret = ERR_UNKNOWN_VLAN;
        break;
      }

      if (cpsw_switch_param->unknown_vlan == ALE_PORT_UNTAGGED_EGRESS) {
        if (!check_valid(cpsw_switch_param->untag_port, MIN_VLAN_UNTAG,
                        MAX_VLAN_UNTAG)) {
          cpsw_config->ret = ERR_VLAN_MCAST_UNTAG;
          break;
        }

        CPSWALEUnknownUntaggedEgressSet(cpswinst->ale_base,
                                        cpsw_switch_param->untag_port);
      } else if (cpsw_switch_param->unknown_vlan ==
                 ALE_PORT_UNKNOWN_REG_MCAST_FLOOD) {
        if (!check_valid(cpsw_switch_param->reg_multi, MIN_VLAN_MCAST_REG,
                        MAX_VLAN_MCAST_REG)) {
          cpsw_config->ret = ERR_VLAN_MCAST_REG;
          break;
        }

        CPSWALEUnknownRegFloodMaskSet(cpswinst->ale_base,
                                      cpsw_switch_param->reg_multi);
      } else if (cpsw_switch_param->unknown_vlan ==
                 ALE_PORT_UNKNOWN_UNREG_MCAST_FLOOD) {
        if (!check_valid(cpsw_switch_param->unreg_multi, MIN_VLAN_MCAST_UNREG,
                        MAX_VLAN_MCAST_UNREG)) {
          cpsw_config->ret = ERR_VLAN_MCAST_UNREG;
          break;
        }

        CPSWALEUnknownUnRegFloodMaskSet(cpswinst->ale_base,
                                        cpsw_switch_param->unreg_multi);
      } else if (cpsw_switch_param->unknown_vlan ==
                 ALE_PORT_UNKNOWN_VLAN_MEMBER) {
        if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK,
                        MIN_PORT_MASK)) {
          cpsw_config->ret = ERR_VLAN_MCAST_UNREG;
          break;
        }

        CPSWALEUnknownMemberListSet(cpswinst->ale_base,
                                    cpsw_switch_param->port_mask);
      }

      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_MACAUTH:
    {
      if (!check_valid(cpsw_switch_param->mac_auth, MIN_MAC_AUTH, MAX_MAC_AUTH)) {
        cpsw_config->ret = ERR_MAC_AUTH;
        break;
      }

      if (!cpsw_switch_param->mac_auth)
        CPSWALEAUTHModeClear(cpswinst->ale_base);
      else
        CPSWALEAUTHModeSet(cpswinst->ale_base);

      cpsw_config->ret = ERR_PASS;
      break;
    }

    case CONFIG_SWITCH_PORT_STATE:
    {
      if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) {
        cpsw_config->ret = ERR_PORT;
        break;
      }

      if (!check_valid(cpsw_switch_param->port_state, MIN_PORT_STATE,
                      MAX_PORT_STATE)) {
        cpsw_config->ret = ERR_PORT_STATE;
        break;
      }

      CPSWALEPortStateSet(cpswinst->ale_base, cpsw_switch_param->port_num,
                          cpsw_switch_param->port_state);
      cpsw_config->ret = ERR_PASS;
      break;
    }
#endif /* CPSW_SWITCH_CONFIG */

    case CONFIG_SWITCH_SET_PORT_CONFIG:
    {
      if (!check_valid(cpsw_phy_param->slv_port_num, MIN_SLV_PORT,
                       MAX_SLV_PORT)) {
        cpsw_config->ret = ERR_SLV_PORT;
        break;
      }

      if (!check_valid(cpsw_phy_param->autoneg, MIN_AUTONEG, MAX_AUTONEG)) {
        cpsw_config->ret = ERR_AUTONEG;
        break;
      }

      if (TRUE == cpsw_phy_param->autoneg) {
        if (!check_valid(cpsw_phy_param->config, MIN_PHY_CONFIG,
                         MAX_PHY_CONFIG)) {
          cpsw_config->ret = ERR_PHY_CONFIG;
          break;
        }
      } else {
        if (!check_valid(cpsw_phy_param->speed, MIN_SPEED, MAX_SPEED)) {
          cpsw_config->ret = ERR_SPEED;
          break;
        }

        if (!check_valid(cpsw_phy_param->duplex, MIN_DUPLEX, MAX_DUPLEX)) {
          cpsw_config->ret = ERR_DUPLEX;
          break;
        }
      }

      if (cpsw_phy_param->autoneg)
        cpswif_phy_autoneg(cpswinst, cpsw_phy_param->slv_port_num,
                                         cpsw_phy_param->config);
      else
        cpswif_phy_forced(cpswinst, cpsw_phy_param->slv_port_num,
                                        cpsw_phy_param->speed,
                                        cpsw_phy_param->duplex);

      cpsw_config->ret = ERR_PASS;
      break;
    }

    default:
      cpsw_config->ret = ERR_INVAL;
      break;
  }
}
