 /* ======================================================================
 *   Copyright (C) 2022 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission
 *   of Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been
 *   supplied.
 * ==================================================================== */

/**
 *  \file   cpsw_ale.c
 *
 *  \brief  This file contains the device abstraction layer API implementation
 *          corresponding to Address Lookup Engine (ALE) module of CPSW
 *          subsystem.
 *
 */

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */


#include "Std_Types.h"

#include "cpsw_priv.h"
#include "cpsw_ale.h"

/* There are static inline functions in hw_types.h file. Map them as well */
#define ETH_START_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
#include "hw_types.h"
#define ETH_STOP_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"

#include "hw_cpsw_ale.h"

#include "Dem.h"
#include "Os.h"

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */

#define CPSW_ALE_TBLW1_ENTRY_TYPE_MASK                     (0x30000000U)
#define CPSW_ALE_TBLW1_ENTRY_TYPE_SHIFT                    (28U)
#define CPSW_ALE_TBLW1_ENTRY_VLAN_ID_MASK                  (0x0FFF0000U)
#define CPSW_ALE_TBLW1_ENTRY_VLAN_ID_SHIFT                 (16U)

/** \brief Multicast MAC address upper byte mask. */
#define CPSW_ALE_TBLW0_VLAN_MEMBER_LIST_MASK               (0x00000007U)
#define CPSW_ALE_TBLW0_VLAN_MEMBER_LIST_SHIFT              (0U)
#define CPSW_ALE_TBLW0_VLAN_UNREG_MCAST_FLOOD_MASK_MASK    (0x00000700U)
#define CPSW_ALE_TBLW0_VLAN_UNREG_MCAST_FLOOD_MASK_SHIFT   (8U)
#define CPSW_ALE_TBLW0_VLAN_REG_MCAST_FLOOD_MASK_MASK      (0x00070000U)
#define CPSW_ALE_TBLW0_VLAN_REG_MCAST_FLOOD_MASK_SHIFT     (16U)
#define CPSW_ALE_TBLW0_VLAN_FORCE_UNTAGGED_EGRESS_MASK     (0x07000000U)
#define CPSW_ALE_TBLW0_VLAN_FORCE_UNTAGGED_EGRESS_SHIFT    (24U)

/** \brief Multicast MAC address upper byte mask. */
#define CPSW_ALE_TBLW0_UNI_ADDR_31_0_MASK                  (0xFFFFFFFFU)
#define CPSW_ALE_TBLW0_UNI_ADDR_31_0_SHIFT                 (0U)
#define CPSW_ALE_TBLW1_UNI_ADDR_47_32_MASK                 (0x0000FFFFU)
#define CPSW_ALE_TBLW1_UNI_ADDR_47_32_SHIFT                (0U)
#define CPSW_ALE_TBLW1_UNI_ADDR_VLAN_ID_MASK               (0x0FFF0000U)
#define CPSW_ALE_TBLW1_UNI_ADDR_VLAN_ID_SHIFT              (16U)
#define CPSW_ALE_TBLW1_UNI_ADDR_TYPE_MASK                  (0xC0000000U)
#define CPSW_ALE_TBLW1_UNI_ADDR_TYPE_SHIFT                 (30U)
#define CPSW_ALE_TBLW2_UNI_ADDR_SECURE_MASK                (0x00000001U)
#define CPSW_ALE_TBLW2_UNI_ADDR_SECURE_SHIFT               (0U)
#define CPSW_ALE_TBLW2_UNI_ADDR_SECURE_ENABLE              (1U)
#define CPSW_ALE_TBLW2_UNI_ADDR_SECURE_DISABLE             (0U)
#define CPSW_ALE_TBLW2_UNI_ADDR_BLOCK_MASK                 (0x00000002U)
#define CPSW_ALE_TBLW2_UNI_ADDR_BLOCK_SHIFT                (1U)
#define CPSW_ALE_TBLW2_UNI_ADDR_BLOCK_ENABLE               (1U)
#define CPSW_ALE_TBLW2_UNI_ADDR_BLOCK_DISABLE              (0U)
#define CPSW_ALE_TBLW2_UNI_ADDR_PORT_NUM_MASK              (0x0000000CU)
#define CPSW_ALE_TBLW2_UNI_ADDR_PORT_NUM_SHIFT             (2U)
#define CPSW_ALE_TBLW2_UNI_ADDR_DLR_MASK                   (0x00000020U)
#define CPSW_ALE_TBLW2_UNI_ADDR_DLR_SHIFT                  (5U)
#define CPSW_ALE_TBLW2_UNI_ADDR_DLR_ENABLE                 (1U)
#define CPSW_ALE_TBLW2_UNI_ADDR_DLR_DISABLE                (0U)

/** \brief Multicast MAC address upper byte mask. */
#define CPSW_ALE_TBLW0_MULTI_ADDR_31_0_MASK                (0xFFFFFFFFU)
#define CPSW_ALE_TBLW0_MULTI_ADDR_31_0_SHIFT               (0U)
#define CPSW_ALE_TBLW1_MULTI_ADDR_47_32_MASK               (0x0000FFFFU)
#define CPSW_ALE_TBLW1_MULTI_ADDR_47_32_SHIFT              (0U)
#define CPSW_ALE_TBLW1_MULTI_ADDR_FWD_STATE_MASK           (0xC0000000U)
#define CPSW_ALE_TBLW1_MULTI_ADDR_FWD_STATE_SHIFT          (30U)
#define CPSW_ALE_TBLW2_MULTI_ADDR_SUPER_MASK               (0x00000002U)
#define CPSW_ALE_TBLW2_MULTI_ADDR_SUPER_SHIFT              (1U)
#define CPSW_ALE_TBLW2_MULTI_ADDR_SUPER_ENABLE             (1U)
#define CPSW_ALE_TBLW2_MULTI_ADDR_SUPER_DISABLE            (0U)
#define CPSW_ALE_TBLW2_MULTI_ADDR_PORT_MASK_MASK           (0x0000001CU)
#define CPSW_ALE_TBLW2_MULTI_ADDR_PORT_MASK_SHIFT          (2U)


/* ========================================================================== */
/*                         Structures and Enums                               */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                 Internal Function Declarations                             */
/* ========================================================================== */

static void CPSWAleEnable(uint32 baseAddr, uint32 aleEnable);

static uint32 CPSWAleGetUntouchEntry(uint32 baseAddr);
static uint32 CPSWAleGetFreeEntry(uint32 baseAddr);
static uint32 CPSWAleGetAddrEntry(uint32      baseAddr,
                                  const uint8 macAddr[],
                                  uint32      vlanId);
static void CPSWAleGetTableEntry(uint32 baseAddr,
                                 uint32 aleTblIdx,
                                 uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS]);
static void CPSWAleSetTableEntry(uint32       baseAddr,
                                 uint32       aleTblIdx,
                                 const uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS]);
/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
#define ETH_START_SEC_CODE
/* MISRAC_2012_R.20.1
  * "Reason - This is the format to use for specifying memory sections " */
#include "Eth_MemMap.h"
void CPSWAleInit(uint32 baseAddr)
{
    CPSWAleEnable(baseAddr, (uint32) TRUE);
    CPSWAleClearTable(baseAddr);

    return;
}

void CPSWAleSetPortState(uint32 baseAddr,
                         uint32 portNum,
                         uint32 portState)
{
    HW_WR_FIELD32((baseAddr + CPSW_ALE_PORT_CONTROL_REG(portNum)),
                  CPSW_ALE_PORT_CONTROL_REG_PORT_STATE, portState);
}

void CPSWAleVlanAwareEnable(
    uint32 baseAddr, uint32 enableVlanAware)
{
    if (((uint32) TRUE) == enableVlanAware)
    {
        HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                      CPSW_ALE_CONTROL_REG_VLAN_AWARE,
                      CPSW_ALE_CONTROL_REG_VLAN_AWARE_MAX);
    }
    else
    {
        HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                      CPSW_ALE_CONTROL_REG_VLAN_AWARE,
                      0x0U);
    }
}

void CPSWAleUnknownRegFloodMaskSet(uint32 baseAddr, uint32 rfmVal)
{
    HW_WR_FIELD32((baseAddr + CPSW_ALE_UNKNOWN_REG_MCAST_FLOOD_REG),
                  CPSW_ALE_UNKNOWN_REG_MCAST_FLOOD_REG_MASK,
                   rfmVal);
}


void CPSWAleUnknownMemberListSet(uint32 baseAddr, uint32 mlVal)
{
    HW_WR_FIELD32((baseAddr + CPSW_ALE_UNKNOWN_VLAN_REG),
                  CPSW_ALE_UNKNOWN_VLAN_REG_LIST,
                   mlVal);
}

 void CPSWAleBypassEnable(
    uint32 baseAddr, uint32 enableBypass)
{
    if (((uint32) TRUE) == enableBypass)
    {
        HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                  CPSW_ALE_CONTROL_REG_BYPASS, CPSW_ALE_CONTROL_REG_BYPASS_MAX);
    }
    else
    {
        HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                      CPSW_ALE_CONTROL_REG_BYPASS, 0U);
    }
}

 uint32 CPSWAleUnicastEntryAdd(
    uint32                            baseAddr,
    const uint8                       macAddr[],
    uint32                            vlanId,
    uint32                            portNum,
    const cpswAleUcastConfigParams_t *unicastParams)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS] = {0U, 0U, 0U};
    uint32 idx    = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;
    uint16 cnt = 0U;

    if ((uint32) TRUE ==
        CPSWAleValidateMacAddr(&macAddr[0U], ETH_MACADDR_TYPE_UNICAST))
    {
        idx = CPSWAleGetAddrEntry(baseAddr, &macAddr[0U], vlanId);
        if (CPSW_ALE_ENTRY_MAX == idx)
        {
            idx = CPSWAleGetFreeEntry(baseAddr);
            if (CPSW_ALE_ENTRY_MAX == idx)
            {
                idx = CPSWAleGetUntouchEntry(baseAddr);
            }
        }
        else
        {
            CPSWAleGetTableEntry(baseAddr, idx, aleEntry);
        }
    }

    if (CPSW_ALE_ENTRY_MAX != idx)
    {
        retVal = idx;

        if (0U == vlanId)
        {
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE,
                           (uint32) CPSW_ALE_TBL_ENTRY_TYPE_ADDR);
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_VLAN_ID,
                           0U);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE,
                           CPSW_ALE_TBL_ENTRY_TYPE_VLAN_ADDR);
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_VLAN_ID,
                           vlanId);
        }

        HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_UNI_ADDR_TYPE,
                       unicastParams->unicastType);

        for (cnt = 0U; cnt < ETH_MAC_ADDR_LEN; cnt++)
        {
            *(((uint8 *) aleEntry) + cnt) =
                macAddr[ETH_MAC_ADDR_LEN - cnt - 1U];
        }

        HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_PORT_NUM, portNum);

        if (((uint32) TRUE) == unicastParams->enableSecure)
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_SECURE,
                           CPSW_ALE_TBLW2_UNI_ADDR_SECURE_ENABLE);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_SECURE,
                           CPSW_ALE_TBLW2_UNI_ADDR_SECURE_DISABLE);
        }

        if (((uint32) TRUE) == unicastParams->enableBlock)
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_BLOCK,
                           CPSW_ALE_TBLW2_UNI_ADDR_BLOCK_ENABLE);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_BLOCK,
                           CPSW_ALE_TBLW2_UNI_ADDR_BLOCK_DISABLE);
        }

        if (((uint32) TRUE) == unicastParams->enableDlr)
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_DLR,
                           CPSW_ALE_TBLW2_UNI_ADDR_DLR_ENABLE);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_UNI_ADDR_DLR,
                           CPSW_ALE_TBLW2_UNI_ADDR_DLR_DISABLE);
        }

        CPSWAleSetTableEntry(baseAddr, idx, aleEntry);
    }

    return (retVal);
}

uint32 CPSWAleUnicastEntryDel(uint32      baseAddr,
                              const uint8 macAddr[],
                              uint32      vlanId,
                              uint32      portNum)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS] = {0U, 0U, 0U};
    uint32 idx    = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;

    if ((uint32) TRUE ==
        CPSWAleValidateMacAddr(&macAddr[0U], ETH_MACADDR_TYPE_UNICAST))
    {
        idx = CPSWAleGetAddrEntry(baseAddr, &macAddr[0U], vlanId);

        while (CPSW_ALE_ENTRY_MAX != idx)
        {
            CPSWAleGetTableEntry(baseAddr, idx, aleEntry);

            if (portNum == HW_GET_FIELD(aleEntry[2U],
                                        CPSW_ALE_TBLW2_UNI_ADDR_PORT_NUM))
            {
                break;
            }
            idx = CPSWAleGetAddrEntry(baseAddr, &macAddr[0U], vlanId);
        }
    }

    if (CPSW_ALE_ENTRY_MAX != idx)
    {
        HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE,
                       CPSW_ALE_TBL_ENTRY_TYPE_FREE);
        CPSWAleSetTableEntry(baseAddr, idx, aleEntry);
        retVal = idx;
    }

    return (retVal);
}

uint32 CPSWAleMulticastEntryAdd(
    uint32                            baseAddr,
    const uint8                       macAddr[],
    uint32                            vlanId,
    uint32                            portMask,
    const cpswAleMcastConfigParams_t *multicastParams)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS] = {0U, 0U, 0U};
    uint32 cnt = 0U;
    uint32 idx = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 regFieldVal = 0U;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 macAddrMultiCast, macAddrBroadCast;

    macAddrMultiCast = CPSWAleValidateMacAddr(&macAddr[0U],
                                              ETH_MACADDR_TYPE_MULTICAST);
    
    macAddrBroadCast = CPSWAleValidateMacAddr(&macAddr[0U],
                                              ETH_MACADDR_TYPE_BROADCAST);
    
    if ((((uint32) TRUE) == macAddrMultiCast) ||
        (((uint32) TRUE) == macAddrBroadCast))
    {
        idx = CPSWAleGetAddrEntry(baseAddr, &macAddr[0U], vlanId);
        if (CPSW_ALE_ENTRY_MAX == idx)
        {
            idx = CPSWAleGetFreeEntry(baseAddr);
            if (CPSW_ALE_ENTRY_MAX == idx)
            {
                idx = CPSWAleGetUntouchEntry(baseAddr);
            }
        }
        else
        {
            //CPSWAleGetTableEntry(baseAddr, idx, aleEntry);
        }
    }

    if (CPSW_ALE_ENTRY_MAX != idx)
    {
        CPSWAleGetTableEntry(baseAddr, idx, aleEntry);
        retVal = idx;

        if (0U == vlanId)
        {
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE,
                           CPSW_ALE_TBL_ENTRY_TYPE_ADDR);
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_VLAN_ID,
                           0U);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE,
                           CPSW_ALE_TBL_ENTRY_TYPE_VLAN_ADDR);
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_VLAN_ID,
                           vlanId);
        }

        for (cnt = 0U; cnt < ETH_MAC_ADDR_LEN; cnt++)
        {
            *(((uint8 *) aleEntry) + cnt) =
                macAddr[ETH_MAC_ADDR_LEN - cnt - 1U];
        }

        regFieldVal = HW_GET_FIELD(aleEntry[2U],
                                   CPSW_ALE_TBLW2_MULTI_ADDR_PORT_MASK);
        regFieldVal |= portMask;
        HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_MULTI_ADDR_PORT_MASK,
                       regFieldVal);

        HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_MULTI_ADDR_FWD_STATE,
                       multicastParams->fwdState);

        if (((uint32) TRUE) == multicastParams->enableSuper)
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_MULTI_ADDR_SUPER,
                           CPSW_ALE_TBLW2_MULTI_ADDR_SUPER_ENABLE);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_MULTI_ADDR_SUPER,
                           CPSW_ALE_TBLW2_MULTI_ADDR_SUPER_DISABLE);
        }

        CPSWAleSetTableEntry(baseAddr, idx, aleEntry);
    }

    return (retVal);
}

/******************************************************************************/
/*                      Internal Functions                                    */
/******************************************************************************/
/**
 * \brief   This API enables/disables packet processing by ALE.
 *
 * \param   baseAddr    Base address of the CPSW.
 * \param   aleEnable   ALE packet processing.
 *                      # TRUE  - Enable ALE packet processing.
 *                      # FALSE - Drop all packets.
 */
static void CPSWAleEnable(
    uint32 baseAddr, uint32 aleEnable)
{
    if (((uint32) TRUE) == aleEnable)
    {
        /* Enable address lookup engine. */
        HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG), 
		              CPSW_ALE_CONTROL_REG_ENABLE,
                      CPSW_ALE_CONTROL_REG_ENABLE_MAX);
    }
    else
    {
        /* Disable address lookup engine. */
        HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG), 
		              CPSW_ALE_CONTROL_REG_ENABLE,
                      0U);
    }

    return;
}


void CPSWAleClearTable(uint32 baseAddr)
{
    StatusType status;
    TickType   startCount = 0U, tempCount, elaspsedCount = 0U;
    /* Clear address lookup engine table. */
    HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG), CPSW_ALE_CONTROL_REG_CLEAR_TABLE,
                  CPSW_ALE_CONTROL_REG_CLEAR_TABLE_MAX);

    status = GetCounterValue(ETH_OS_COUNTER_ID, &startCount);
    if (((StatusType) E_OK) == status)
    {
        /*
         * Wait till the lookup engine table is cleared. Access to all ALE
         ******registers
         * will be blocked (wait states) until the 64 clocks have completed.
         */
        while (CPSW_ALE_CONTROL_REG_CLEAR_TABLE_MAX ==
               (uint32) HW_RD_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                                      CPSW_ALE_CONTROL_REG_CLEAR_TABLE))
        {
            /* Below API can change start time, so use temp variable */
            tempCount = startCount;
            status    = GetElapsedValue(
                ETH_OS_COUNTER_ID,
                &tempCount,
                &elaspsedCount);
            if ((((StatusType) E_OK) != status) ||
                (elaspsedCount >= ETH_TIMEOUT_DURATION))
            {
                /* timeout */
#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
                Dem_SetEventStatus(
                    ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
                break;
            }
        }
    }
    return;
}

/**
 * \brief   This API sets an ALE table entry at given index.
 *
 * \param   baseAddr     Base address of the CPSW.
 * \param   aleTblIdx    The Index of the table entry.
 * \param   pAleEntryPtr The address of the entry to be set.
 */
static void CPSWAleSetTableEntry(
    uint32       baseAddr,
    uint32       aleTblIdx,
    const uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS])
{
    uint32 cnt    = 0U;
    uint32 regVal = 0U;

    for (cnt = 0U; cnt < CPSW_ALE_ENTRY_NUM_WORDS; cnt++)
    {
        HW_WR_REG32(baseAddr + CPSW_ALE_TABLE_WORD(cnt),
                    aleEntry[cnt]);
    }

    HW_SET_FIELD32(regVal, CPSW_ALE_TABLE_CONTROL_REG_ENTRY_POINTER, aleTblIdx);
    HW_SET_FIELD32(regVal, CPSW_ALE_TABLE_CONTROL_REG_WRITE_RDZ,
                   CPSW_ALE_TABLE_CONTROL_REG_WRITE_RDZ_MAX);
    HW_WR_REG32(baseAddr + CPSW_ALE_TABLE_CONTROL_REG, regVal);
}

/**
 * \brief   This API reads an ALE table entry at given index.
 *
 * \param   baseAddr     Base address of the CPSW.
 * \param   aleTblIdx    The Index of the table entry.
 * \param   pAleEntryPtr The address where the ALE entry to be read.
 */
static void CPSWAleGetTableEntry(uint32 baseAddr,
                                 uint32 aleTblIdx,
                                 uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS])
{
    uint32 cnt = 0U;

    HW_WR_REG32(baseAddr + CPSW_ALE_TABLE_CONTROL_REG, aleTblIdx);

    for (cnt = 0U; cnt < CPSW_ALE_ENTRY_NUM_WORDS; cnt++)
    {
        aleEntry[cnt] =
            HW_RD_REG32(baseAddr + CPSW_ALE_TABLE_WORD(cnt));
    }
}

/**
 * \brief   Gives the index of the ALE entry which is free
 *
 * \param   baseAddr Base address of the CPSW.
 *
 * \retval  CPSW_ALE_ENTRY_MAX  Entry not found
 * \retval  index    Index of the ALE entry which is free.
 */
  static uint32 CPSWAleGetFreeEntry(uint32 baseAddr)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS];
    uint32 idx    = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;

    /* Check which ALE entry is free starting from 0th entry */
    for (idx = 0U; idx < CPSW_ALE_ENTRY_MAX; idx++)
    {
        CPSWAleGetTableEntry(baseAddr, idx, aleEntry);

        /* Break if the table entry is free */
        if (CPSW_ALE_TBL_ENTRY_TYPE_FREE ==
            (cpswAleTblEntryType_t)HW_GET_FIELD(aleEntry[1U],
                                                   CPSW_ALE_TBLW1_ENTRY_TYPE))
        {
            retVal = idx;
            break;
        }
    }

    return (retVal);
}

/**
 * \brief   Gives the index of the ALE entry which is untouched ageable
 *
 * \param   baseAddr Base address of the CPSW.
 *
 * \retval  CPSW_ALE_ENTRY_MAX  Entry not found
 * \retval  index    Index of the ageable Unicast ALE entry which is untouched.
 */

 static uint32 CPSWAleGetUntouchEntry(uint32 baseAddr)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS];
    uint32 idx    = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 entryType;

    /* Check which ALE entry is free starting from 0th entry */
    for (idx = 0U; idx < CPSW_ALE_ENTRY_MAX; idx++)
    {
        CPSWAleGetTableEntry(baseAddr, idx, aleEntry);
        entryType = HW_GET_FIELD(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE);

        /* Goto next entry if the ale entry is not valid address */
        if ((CPSW_ALE_TBL_ENTRY_TYPE_FREE != entryType) &&
            (CPSW_ALE_TBL_ENTRY_TYPE_VLAN != entryType))
        {
            if ((uint32) TRUE == CPSWAleValidateMacAddr((uint8 *) aleEntry,
                                                     ETH_MACADDR_TYPE_UNICAST))
            {
                entryType = HW_GET_FIELD(aleEntry[1U],
                                         CPSW_ALE_TBLW1_UNI_ADDR_TYPE);

                if (((uint32) CPSW_ALE_TBL_UNICAST_TYPE_AGEABLE_UNTOUCH) ==
                    entryType)
                {
                    retVal = idx;
                    break;
                }
            }
        }
    }

    return (retVal);
}


/**
 * \brief  Gives the index of the ALE entry which match address of VLAN
 *
 * \param  baseAddr  Base address of the CPSW.
 * \param  pMacAddr  Ethernet address
 * \param  vlanId    VLAN ID
 *
 * \retval index of the ALE entry which match address of VLAN
 * \retval CPSW_ALE_ENTRY_MAX if entry not found
 */
    static uint32 CPSWAleGetAddrEntry(uint32      baseAddr,
                                  const uint8 macAddr[],
                                  uint32      vlanId)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS];
    uint32 entryType = 0U;
    uint32 idx = 0U, cnt = 0U;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;

    /* Check which ALE entry is free starting from 0th entry */
    for (idx = 0U;
         ((idx < CPSW_ALE_ENTRY_MAX) && (CPSW_ALE_ENTRY_MAX == retVal));
         idx++)
    {
        CPSWAleGetTableEntry(baseAddr, idx, aleEntry);

        entryType = HW_GET_FIELD(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE);

        /* Goto next entry if the ale entry is not valid address */
        if (((uint32)CPSW_ALE_TBL_ENTRY_TYPE_FREE != entryType) &&
            ((uint32)CPSW_ALE_TBL_ENTRY_TYPE_VLAN != entryType))
        {
            if (HW_GET_FIELD(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_VLAN_ID) ==
                vlanId)
            {
                for (cnt = 0U; cnt < (uint32)ETH_MAC_ADDR_LEN; cnt++)
                {
                    if ((*(((uint8 *) aleEntry) + cnt)) !=
                        macAddr[ETH_MAC_ADDR_LEN - cnt - (uint32) 1U])
                    {
                        break;
                    }
                    else if (ETH_MAC_ADDR_LEN == (cnt + (uint32) 1U))
                    {
                        retVal = idx;
                    }
                    else
                    {
                        /*
                         * Nothing to do as check for valid entry is done
                         * already
                         */
                    }
                }
            }
        }
    }

    return (retVal);
}

 uint32 CPSWAleValidateMacAddr(
    const uint8 macAddr[ETH_MAC_ADDR_LEN], uint32 addrType)

{
    uint32 cnt    = 0U;
    uint32 retVal = FALSE;

    for (cnt = 0U; cnt < (uint32)ETH_MAC_ADDR_LEN; cnt++)
    {
        if (CPSW_ALE_BROADCAST_MAC_ADDR_MASK !=
            (macAddr[cnt] & CPSW_ALE_BROADCAST_MAC_ADDR_MASK))
        {
            break;
        }
    }

    if ((ETH_MAC_ADDR_LEN == cnt) &&
        ((uint32)ETH_MACADDR_TYPE_BROADCAST == addrType))
    {
        retVal = (uint32) TRUE;
    }
    else if (ETH_MAC_ADDR_LEN == cnt)
    {
        retVal = (uint32) FALSE;
    }
    else if (((uint32) ETH_MACADDR_TYPE_BROADCAST) == addrType)
    {
        retVal = (uint32) FALSE;
    }
    else
    {
        if (((uint32) ETH_MACADDR_TYPE_MULTICAST) == addrType)
        {
            if (CPSW_ALE_MULTICAST_MAC_ADDR_MASK ==
                (macAddr[0U] & CPSW_ALE_MULTICAST_MAC_ADDR_MASK))
            {
                retVal = (uint32) TRUE;
            }
        }
        else
        {
            /* unicast */
            if (CPSW_ALE_MULTICAST_MAC_ADDR_MASK !=
                (macAddr[0U] & CPSW_ALE_MULTICAST_MAC_ADDR_MASK))
            {
                retVal = (uint32) TRUE;
            }
        }
    }

    return (retVal);
}

void CPSWAleAgeOut(uint32 baseAddr)
{
    StatusType status;
    TickType   startCount = 0U, tempCount, elaspsedCount = 0U;
    /* Enable age out of address lookup engine table. */
    HW_WR_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                  CPSW_ALE_CONTROL_REG_AGE_OUT_NOW,
                  CPSW_ALE_CONTROL_REG_AGE_OUT_NOW_MAX);

    status = GetCounterValue(ETH_OS_COUNTER_ID, &startCount);
    if (((StatusType) E_OK) == status)
    {
        while (CPSW_ALE_CONTROL_REG_AGE_OUT_NOW_MAX ==
               (uint32) HW_RD_FIELD32((baseAddr + CPSW_ALE_CONTROL_REG),
                                      CPSW_ALE_CONTROL_REG_AGE_OUT_NOW))
        {
            /* Below API can change start time, so use temp variable */
            tempCount = startCount;
            status    = GetElapsedValue(
                ETH_OS_COUNTER_ID,
                &tempCount,
                &elaspsedCount);
            if ((((StatusType) E_OK) != status) ||
                (elaspsedCount >= ETH_TIMEOUT_DURATION))
            {
                /* timeout */
#if (ETH_E_HARDWARE_ERROR != ETH_DEM_NO_EVENT)
                Dem_SetEventStatus(
                    ETH_E_HARDWARE_ERROR, DEM_EVENT_STATUS_FAILED);
#endif
                break;
            }
        }
    }
    return;
}


uint32 CPSWAleMulticastEntryPortDel(uint32      baseAddr,
                                    const uint8 macAddr[],
                                    uint32      vlanId,
                                    uint8      portNum)
{
    uint32 aleEntry[CPSW_ALE_ENTRY_NUM_WORDS] = {0U, 0U, 0U};
    uint32 idx = (uint32) CPSW_ALE_ENTRY_MAX;
    uint32 regFieldVal = 0U;
    uint32 retVal = (uint32) CPSW_ALE_ENTRY_MAX;

    if ((uint32) TRUE ==
        CPSWAleValidateMacAddr(&macAddr[0U], ETH_MACADDR_TYPE_MULTICAST))
    {
        idx = CPSWAleGetAddrEntry(baseAddr, &macAddr[0U], vlanId);
    }

    if (CPSW_ALE_ENTRY_MAX != idx)
    {
        CPSWAleGetTableEntry(baseAddr, idx, aleEntry);

        regFieldVal = HW_GET_FIELD(aleEntry[2U],
                                   CPSW_ALE_TBLW2_MULTI_ADDR_PORT_MASK);
        regFieldVal &= ~((uint32) 1U << (uint32) portNum);

        if (0U == regFieldVal)
        {
            HW_SET_FIELD32(aleEntry[1U], CPSW_ALE_TBLW1_ENTRY_TYPE,
                           CPSW_ALE_TBL_ENTRY_TYPE_FREE);
            CPSWAleSetTableEntry(baseAddr, idx, aleEntry);
        }
        else
        {
            HW_SET_FIELD32(aleEntry[2U], CPSW_ALE_TBLW2_MULTI_ADDR_PORT_MASK,
                           regFieldVal);
            CPSWAleSetTableEntry(baseAddr, idx, aleEntry);
        }
        retVal = idx;
    }

    return (retVal);
}

#define ETH_STOP_SEC_CODE
#include "Eth_MemMap.h"
