/*
 *  Copyright (c) Texas Instruments Incorporated 2023-2024
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    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.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "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 COPYRIGHT
 *  OWNER OR CONTRIBUTORS 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.
 */

/*!
 * \file  enetapp.c
 *
 * \brief This file contains the implementation of the Enet TSN example.
 */

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

#include <stdint.h>
#include <tsn_combase/combase.h>
#include <tsn_combase/combase_link.h>
#include <tsn_combase/tilld/cb_lld_ethernet.h>
#include "nrt_flow/dataflow.h"
#include "debug_log.h"
#include "tsninit.h"
#include "enetapp_cpsw.h"

#define TF_USE_GENF true


/* ========================================================================== */
/*                              Global Variables                              */
/* ========================================================================== */

EnetApp_Cfg gEnetAppCfg =
{
    .name = ENETAPP_DEFAULT_CFG_NAME,
};

static const uint8_t BROADCAST_MAC_ADDRESS[ENET_MAC_ADDR_LEN] = {
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

#if TF_USE_GENF
#include <drivers/pinmux.h>
static Pinmux_PerCfg_t gCustomPinMux[] = {
        /* ECAP0_IN_APWM_OUT (D18) - set desired mode here */
        {
                PIN_EXT_REFCLK1,
            ( PIN_MODE(1) | PIN_PULL_DISABLE )
        },
        {PINMUX_END, PINMUX_END}
    };
#endif


/* these vars are shared with gptp task to configure gptp, put it in the global mem */
static char g_netdevices[MAX_NUM_MAC_PORTS][CB_MAX_NETDEVNAME] = {0};
/* ========================================================================== */
/*                           Function Declarations                            */
/* ========================================================================== */

static void EnetApp_updateCfg(EnetApp_Cfg *enet_cfg)
{
    EnetApp_getEnetInstInfo(CONFIG_ENET_CPSW0, &enet_cfg->enetType, &enet_cfg->instId);
    EnetApp_getEnetInstMacInfo(enet_cfg->enetType, enet_cfg->instId,
                               enet_cfg->macPorts, &enet_cfg->numMacPorts);
}

#define LOG_BUFFER_SIZE (1024)
void ConsolePrint(const char *pcString, ...)
{
    /* Use DebugP_log() because EnetAppUtils_print() has limit bufsize */
    va_list args;
    char buffer[LOG_BUFFER_SIZE];

    va_start(args, pcString);
    vsnprintf(buffer, sizeof(buffer), pcString, args);
    va_end(args);

    DebugP_log("%s", buffer);
}

int EnetApp_initTsn(void)
{
    lld_ethdev_t ethdevs[MAX_NUMBER_ENET_DEVS] = {0};
    int i;
    int res = 0;
    AppTsnCfg_t appCfg =
    {
        .consoleOutCb = ConsolePrint,
    };

    for (i = 0; i < gEnetAppCfg.numMacPorts; i++)
    {
        snprintf(&g_netdevices[i][0], CB_MAX_NETDEVNAME, "tilld%d", i);
        appCfg.netdevs[i] = &g_netdevices[i][0];
        ethdevs[i].netdev = g_netdevices[i];
        ethdevs[i].macport = gEnetAppCfg.macPorts[i];
        if (i == 0)
        {
            /* tilld0 reuses the allocated source mac, other interfaces will allocate
             * the mac by themself */
            memcpy(ethdevs[i].srcmac, gEnetAppCfg.macAddr, ENET_MAC_ADDR_LEN);
        }
    }
    appCfg.netdevs[i] = NULL;
    if (EnetApp_initTsnByCfg(&appCfg) < 0)
    {
        EnetAppAbort("Failed to int tsn!\r\n");
    }
    if (cb_lld_init_devs_table(ethdevs, i, gEnetAppCfg.enetType,
                               gEnetAppCfg.instId, ENET_SYSCFG_TIMESTAMP_SOURCE) < 0)
    {
        EnetAppAbort("Failed to int devs table!\r\n");
    }
    cb_socket_set_lldcfg_update_cb(EnetApp_lldCfgUpdateCb);

    if (EnetApp_startTsn() < 0)
    {
        EnetAppAbort("Failed to start TSN App!\r\n");
    }
    EnetAppUtils_print("%s:TSN app start done!\r\n", __func__);

    return res;
}

void EnetApp_printCpuLoad(void)
{
    static uint32_t startTime_ms = 0;
    const  uint32_t currTime_ms  = ClockP_getTimeUsec()/1000;
    const  uint32_t printInterval_ms = 5000;

    if (startTime_ms == 0)
    {
        startTime_ms = currTime_ms;
    }
    else if ((currTime_ms - startTime_ms) > printInterval_ms)
    {
        const uint32_t cpuLoad = TaskP_loadGetTotalCpuLoad();

        DebugP_log(" %6d.%3ds : CPU load = %3d.%02d %%\r\n",
                  currTime_ms/1000, currTime_ms%1000,
                  cpuLoad/100, cpuLoad%100 );

        startTime_ms = currTime_ms;
        TaskP_loadResetAll();
    }
    return;
}

#ifdef TF_USE_GENF

/* GENF Configuration Parameters */
#define GENF_OUTPUT_FREQ_HZ         (40000U)     /* 40 KHz output frequency */
#define CPTS_RFTCLK_FREQ_HZ         (200000000U) /* 200 MHz reference clock for AM64x */
#define GENF_INDEX_1                (0U)         /* Use GENF0 */
#define GENF_INDEX_2                (1U)         /* Use GENF1 */
#define GENF_POLARITY_HIGH          (1U)
#define GENF_POLARITY_LOW           (0U)

/* Timesync Router Configuration for AM243x/AM64x
 * The TIMESYNC_EVENT_INTROUTER0 routes CPTS events to output pins/signals.
 * GENF0 (input 21) is routed to SYNC0_OUT pin for external sync output.
 * GENF1 (input 22) is routed to HW_PUSH_EVENT0 (input 30 on router output)
 * so the CPTS captures a timestamp on each GENF1 toggle — readable via
 * the CPSW_CPTS_IOCTL_REGISTER_HWPUSH_CALLBACK callback (getTsval).
 */
#define TIMESYNC_INTRTR0_BASE       (CSL_TIMESYNC_EVENT_INTROUTER0_CFG_BASE)
#define TIMESYNC_IN_CPSW0_GENF0     (CSLR_TIMESYNC_EVENT_INTROUTER0_IN_CPSW0_CPTS_GENF0_0)  /* router input 21: CPSW GENF0 */
#define TIMESYNC_IN_CPSW0_GENF1     (CSLR_TIMESYNC_EVENT_INTROUTER0_IN_CPSW0_CPTS_GENF1_0)  /* router input 22: CPSW GENF1 */
#define TIMESYNC_OUT_SYNC0_PIN      (24U)        /* router output 24: SYNC0_OUT physical pin */
#define TIMESYNC_OUT_HW_EVENT0      (30U)        /* router output 30: CPTS HW_PUSH input 0 */


uint32_t tsindex = 0;
uint64_t tsVal[20] = {0};

/**
 * @brief HW push event callback — invoked by CPTS driver when a HW_PUSH
 *        input is asserted (GENF1 toggle routed via Timesync Router).
 *
 * Called from interrupt context. Captures the raw 64-bit GTC counter value
 * (not CPTS nanoseconds) into a 20-entry ring buffer for later inspection.
 * GTC_getCount64() is used here because it is safe to call from ISR context;
 * ENET IOCTL calls are not safe from ISR context.
 *
 * @param cbArg      hEnet handle passed at registration (see ConfigureGenF)
 * @param hwPushNum  HW push instance number that fired (CPSW_CPTS_HWPUSH_1)
 */
void getTsval(void *cbArg, CpswCpts_HwPush hwPushNum)
{
    tsVal[tsindex % 20] = GTC_getCount64();
    tsindex++;
}

/**
 * @brief Configure CPTS GENF (Generic Function) output
 *
 * @param hEnet     Enet driver handle
 * @param freqHz    Desired output frequency in Hz
 * @param enable    Enable (true) or disable (false) GENF
 * @return int32_t  ENET_SOK on success
 */
int32_t ConfigureGenF(Enet_Handle hEnet, uint32_t freqHz, bool enable)
{
    int32_t status;
    uint32_t coreId = EnetSoc_getCoreId();
    Enet_IoctlPrms prms;
    CpswCpts_SetFxnGenInArgs genFArgs;
    uint64_t tsCompVal;
    uint32_t genFLength;
    uint64_t currentTs = 0;
    CpswCpts_RegisterHwPushCbInArgs hwPushArgs;

    /* Guard against disable path calling with freqHz=0 */
    if (freqHz == 0U)
    {
        freqHz = 1U;  /* length will be overridden to 0 in the else branch below */
    }

    /* Calculate GENF parameters based on desired frequency */
    /* GENF toggles every 'length' RFTCLK cycles, so length = half-period */
    /* Length = RFTCLK_FREQ / (OUTPUT_FREQ) */
    genFLength = CPTS_RFTCLK_FREQ_HZ / (freqHz);

    /* Get current CPTS timestamp to set initial compare value */
    ENET_IOCTL_SET_OUT_ARGS(&prms, &currentTs);
    ENET_IOCTL(hEnet, coreId, ENET_TIMESYNC_IOCTL_GET_CURRENT_TIMESTAMP, &prms, status);

    if (status != ENET_SOK)
    {
        EnetAppUtils_print("Failed to get current timestamp: %d\r\n", status);
        return status;
    }

    /* Set compare value to current timestamp + 1 second to start in future */
    tsCompVal = currentTs + 1000000000ULL; /* Add 1 second in nanoseconds */

    /* Configure GENF parameters */
    genFArgs.index = GENF_INDEX_1;
    genFArgs.length = genFLength;
    genFArgs.compare = tsCompVal;
    genFArgs.polarityInv = GENF_POLARITY_HIGH;
    genFArgs.ppmVal  = 0U;
    genFArgs.ppmDir  = CPSW_CPTS_GENF_PPM_ADJDIR_DECREASE;
    genFArgs.ppmMode = ENET_TIMESYNC_ADJMODE_DISABLE;

    if (enable)
    {
        EnetAppUtils_print("Configuring GENF%d:\r\n", GENF_INDEX_1);
        EnetAppUtils_print("  Output Frequency: %u Hz\r\n", freqHz);
        EnetAppUtils_print("  Length: %u RFTCLK cycles\r\n", genFLength);
        EnetAppUtils_print("  Compare Value: %llu ns\r\n", tsCompVal);
        EnetAppUtils_print("  Polarity: %s\r\n", genFArgs.polarityInv ? "High" : "Low");

        /* Set GENF configuration */
        ENET_IOCTL_SET_IN_ARGS(&prms, &genFArgs);
        ENET_IOCTL(hEnet, coreId, CPSW_CPTS_IOCTL_SET_GENF, &prms, status);

        if (status != ENET_SOK)
        {
            EnetAppUtils_print("Failed to configure GENF-%d: %d\r\n", genFArgs.index, status);
            return status;
        }

        EnetAppUtils_print("GENF%d configured and enabled successfully\r\n", genFArgs.index);

        /* Set GENF configuration */
        genFArgs.index = GENF_INDEX_2;
        ENET_IOCTL_SET_IN_ARGS(&prms, &genFArgs);
        ENET_IOCTL(hEnet, coreId, CPSW_CPTS_IOCTL_SET_GENF, &prms, status);

        if (status != ENET_SOK)
        {
            EnetAppUtils_print("Failed to configure GENF-%d: %d\r\n", genFArgs.index, status);
            return status;
        }

        EnetAppUtils_print("GENF%d configured and enabled successfully\r\n", genFArgs.index);
    }
    else
    {
        /* Disable GENF by setting length to 0 */
        genFArgs.length = 0;
        ENET_IOCTL_SET_IN_ARGS(&prms, &genFArgs);
        ENET_IOCTL(hEnet, coreId, CPSW_CPTS_IOCTL_SET_GENF, &prms, status);

        EnetAppUtils_print("GENF%d disabled\r\n", GENF_INDEX);
    }

    /* Register HW push callback for CPSW_CPTS_HWPUSH_1.
     * GENF1 is routed to HW_PUSH_EVENT0 by ConfigureTimesyncRouter, which
     * asserts the CPTS HW_PUSH_1 input on each GENF1 toggle. The CPTS driver
     * then invokes getTsval from interrupt context on every such event.
     * hEnet is passed as cbArg so the callback can issue IOCTL calls if needed
     * in a deferred (task-context) handler in the future.
     */
    hwPushArgs.hwPushNotifyCb    = getTsval;
    hwPushArgs.hwPushNotifyCbArg = (void *)hEnet;
    hwPushArgs.hwPushNum         = CPSW_CPTS_HWPUSH_1;

    ENET_IOCTL_SET_IN_ARGS(&prms, &hwPushArgs);
    ENET_IOCTL(hEnet, coreId, CPSW_CPTS_IOCTL_REGISTER_HWPUSH_CALLBACK, &prms, status);


    return status;
}

/**
 * @brief Configure Timesync Router to route GENF to SYNC0_OUT pin
 *
 * @param inputSrc   Timesync router input source
 * @param outputDest Timesync router output destination
 * @return int32_t   CSL_PASS on success
 */
int32_t ConfigureTimesyncRouter(uint32_t inputSrc, uint32_t outputDest)
{
    int32_t                             retVal;
    struct tisci_msg_rm_irq_set_req     rmIrqReq;
    struct tisci_msg_rm_irq_set_resp    rmIrqResp;
    /* Route 1: CPSW GENF0 → SYNC0_OUT physical pin
     * src_id / dst_id = 6 is TISCI_DEV_TIMESYNC_EVENT_INTROUTER0 (AM243x RM).
     * src_index 21 = GENF0 input to the Timesync Router.
     * dst_host_irq 24 = router output wired to the SYNC0_OUT pad.
     */
    rmIrqReq.valid_params           = 0U;
    rmIrqReq.valid_params          |= TISCI_MSG_VALUE_RM_DST_ID_VALID;
    rmIrqReq.valid_params          |= TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
    rmIrqReq.global_event           = 0U;
    rmIrqReq.src_id                 = 6U;  /* TISCI_DEV_TIMESYNC_EVENT_INTROUTER0 */
    rmIrqReq.src_index              = CSLR_TIMESYNC_EVENT_INTROUTER0_IN_CPSW0_CPTS_GENF0_0; /* input 21: CPSW GENF0 */
    rmIrqReq.dst_id                 = 6U;  /* TISCI_DEV_TIMESYNC_EVENT_INTROUTER0 */
    rmIrqReq.dst_host_irq           = TIMESYNC_OUT_SYNC0_PIN; /* output 24: SYNC0_OUT pin */
    rmIrqReq.ia_id                  = 0U;
    rmIrqReq.vint                   = 0U;
    rmIrqReq.vint_status_bit_index  = 0U;
    rmIrqReq.secondary_host         = TISCI_MSG_VALUE_RM_UNUSED_SECONDARY_HOST;

    retVal = Sciclient_rmIrqSetRaw(&rmIrqReq, &rmIrqResp, SystemP_WAIT_FOREVER);
    if (retVal != ENET_SOK)
    {
       EnetAppUtils_print("TSR pin: %d routing failed: %d\r\n",
               rmIrqReq.dst_host_irq, retVal);
    }

    /* Route 2: CPSW GENF1 → CPTS HW_PUSH_EVENT0
     * src_index 22 = GENF1 input to the Timesync Router.
     * dst_host_irq 30 = router output connected to the CPTS HW_PUSH_1 input,
     * which triggers the getTsval callback registered via
     * CPSW_CPTS_IOCTL_REGISTER_HWPUSH_CALLBACK.
     */
    rmIrqReq.valid_params           = 0U;
    rmIrqReq.valid_params          |= TISCI_MSG_VALUE_RM_DST_ID_VALID;
    rmIrqReq.valid_params          |= TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
    rmIrqReq.global_event           = 0U;
    rmIrqReq.src_id                 = 6U;  /* TISCI_DEV_TIMESYNC_EVENT_INTROUTER0 */
    rmIrqReq.src_index              = CSLR_TIMESYNC_EVENT_INTROUTER0_IN_CPSW0_CPTS_GENF1_0; /* input 22: CPSW GENF1 */
    rmIrqReq.dst_id                 = 6U;  /* TISCI_DEV_TIMESYNC_EVENT_INTROUTER0 */
    rmIrqReq.dst_host_irq           = TIMESYNC_OUT_HW_EVENT0; /* output 30: CPTS HW_PUSH input */
    rmIrqReq.ia_id                  = 0U;
    rmIrqReq.vint                   = 0U;
    rmIrqReq.vint_status_bit_index  = 0U;
    rmIrqReq.secondary_host         = TISCI_MSG_VALUE_RM_UNUSED_SECONDARY_HOST;


    retVal = Sciclient_rmIrqSetRaw(&rmIrqReq, &rmIrqResp, SystemP_WAIT_FOREVER);
    if (retVal != ENET_SOK)
    {
       EnetAppUtils_print("TSR pin: %d routing failed: %d\r\n",
               rmIrqReq.dst_host_irq, retVal);
    }

    return retVal;
}

uint32_t my_count = 0;
void PrintMyCount()
{
    uint32_t currTime_ms = ClockP_getTimeUsec()/1000;

    DebugP_log("*** INT count: %lu,  %6d.%3d sec\r\n", my_count, currTime_ms/1000, currTime_ms%1000);
}

/**
 * @brief CPTS event notification callback (called from ISR context)
 *
 * Invoked by the CPTS interrupt handler for every event read from the FIFO.
 * For GENF, CPSW_CPTS_EVENTTYPE_TS_COMP fires on each compare match (i.e.
 * each toggle of the GENF output).
 *
 * @param cbArg     Argument passed at registration time
 * @param eventInfo CPTS event details (type, timestamp, port, etc.)
 */
static void App_cptsEventCb(void *cbArg, CpswCpts_Event *eventInfo)
{
    /* This callback runs in interrupt context — no blocking calls, no printf. */
    if (eventInfo->eventType == CPSW_CPTS_EVENTTYPE_TS_COMP)
    {
        /* GENF compare match: output has toggled.
         * eventInfo->tsVal holds the 64-bit CPTS timestamp of the toggle.
         * Signal a task or set a flag here; do not call printf or any
         * blocking/locking API from this context. */
         
    }
    my_count++;
}

/**
 * @brief Register CPTS event callback for GENF ISR notification
 *
 * @param hEnet Enet driver handle
 * @return int32_t ENET_SOK on success
 */
int32_t InitializeCPTS_WithGenF(Enet_Handle hEnet)
{
    int32_t status;
    uint32_t coreId = EnetSoc_getCoreId();
    Enet_IoctlPrms prms;
    CpswCpts_RegisterStackInArgs regStackInArgs;

    regStackInArgs.eventNotifyCb    = App_cptsEventCb;
    regStackInArgs.eventNotifyCbArg = NULL;   /* pass app context here if needed */

    ENET_IOCTL_SET_IN_ARGS(&prms, &regStackInArgs);
    ENET_IOCTL(hEnet, coreId, CPSW_CPTS_IOCTL_REGISTER_STACK, &prms, status);

    if (status != ENET_SOK)
    {
        EnetAppUtils_print("Failed to register CPTS event callback: %d\r\n", status);
        return status;
    }

    EnetAppUtils_print("CPTS event callback registered\r\n");
    return ENET_SOK;
}

/**
 * @brief Main function to setup GENF and route to SYNC0_OUT
 *
 * @param hEnet Enet driver handle (from gptp_cpsw_app)
 * @return int32_t Status code
 */
int32_t SetupGenFWithSync0Out(Enet_Handle hEnet)
{
    int32_t status;

    EnetAppUtils_print("\n====== GENF to SYNC0_OUT Configuration (AM64x) ======\r\n\n");

    /* Step 1: Initialize CPTS */
    status = InitializeCPTS_WithGenF(hEnet);
    if (status != ENET_SOK)
    {
        EnetAppUtils_print("ERROR: CPTS initialization failed\r\n");
        return status;
    }

    /* Step 2: Configure GENF output frequency */
    status = ConfigureGenF(hEnet, GENF_OUTPUT_FREQ_HZ, true);
    if (status != ENET_SOK)
    {
        EnetAppUtils_print("ERROR: GENF configuration failed\r\n");
        return status;
    }

    /* Step 3: Configure Timesync Router to route GENF to SYNC0_OUT */
    status = ConfigureTimesyncRouter(TIMESYNC_IN_CPSW0_GENF0,
                                     TIMESYNC_OUT_SYNC0_PIN);
    if (status != CSL_PASS)
    {
        EnetAppUtils_print("ERROR: Timesync Router configuration failed\r\n");
        return ENET_EFAIL;
    }

    EnetAppUtils_print("\n====== Configuration Complete ======\r\n");
    EnetAppUtils_print("GENF0 signal (%u Hz) is now output on SYNC0_OUT pin\r\n",
           GENF_OUTPUT_FREQ_HZ);
    EnetAppUtils_print("Timesync Router: Input 21 (CPSW0_GENF0) -> Output 24 (SYNC0_OUT)\r\n\n");

    return ENET_SOK;
}

/**
 * @brief Update GENF frequency dynamically
 *
 * @param hEnet  Enet driver handle
 * @param newFreqHz New frequency in Hz
 * @return int32_t Status code
 */
int32_t UpdateGenFFrequency(Enet_Handle hEnet, uint32_t newFreqHz)
{
    EnetAppUtils_print("Updating GENF frequency to %u Hz\r\n", newFreqHz);
    return ConfigureGenF(hEnet, newFreqHz, true);
}

/**
 * @brief Disable GENF output
 *
 * @param hEnet Enet driver handle
 * @return int32_t Status code
 */
int32_t DisableGenF(Enet_Handle hEnet)
{
    EnetAppUtils_print("Disabling GENF output\r\n");
    return ConfigureGenF(hEnet, 0, false);
}
#endif

static void EnetApp_enableTsSync()
{
    Enet_IoctlPrms prms;
    CpswCpts_OutputBitSel bitSelect;
    int32_t status;
#ifdef TF_USE_GENF
    bitSelect = CPSW_CPTS_TS_OUTPUT_BIT_DISABLED;
#else    
    bitSelect = CPSW_CPTS_TS_OUTPUT_BIT_17;
#endif
    ENET_IOCTL_SET_IN_ARGS(&prms, &bitSelect);
    ENET_IOCTL(gEnetAppCfg.hEnet, gEnetAppCfg.coreId, CPSW_CPTS_IOCTL_SELECT_TS_OUTPUT_BIT, &prms, status);
    if (status != ENET_SOK)
    {
        EnetAppUtils_print("%s: Failed to set TS SYNC OUT BIT : %d\r\n", gEnetAppCfg.name, status);
    }
    return;
}

void EnetApp_initAppCfg(EnetPer_AttachCoreOutArgs *attachArgs, EnetApp_HandleInfo *handleInfo)
{
    /* To support gptp switch mode, we must configure from syscfg file:
     * enet_cpsw1.DisableMacPort2 = false; */
    EnetApp_updateCfg(&gEnetAppCfg);

    gEnetAppCfg.coreId = EnetSoc_getCoreId();
    EnetQueue_initQ(&gEnetAppCfg.txFreePktInfoQ);
    EnetAppUtils_enableClocks(gEnetAppCfg.enetType, gEnetAppCfg.instId);
    DebugP_log("start to open driver.\r\n");
    EnetApp_driverInit();
    EnetApp_driverOpen(gEnetAppCfg.enetType, gEnetAppCfg.instId);
    EnetApp_acquireHandleInfo(gEnetAppCfg.enetType, gEnetAppCfg.instId, handleInfo);
    gEnetAppCfg.hEnet = handleInfo->hEnet;
    EnetApp_coreAttach(gEnetAppCfg.enetType, gEnetAppCfg.instId, gEnetAppCfg.coreId, attachArgs);
    gEnetAppCfg.coreKey = attachArgs->coreKey;

    EnetApp_enableTsSync();
#ifdef TF_USE_GENF
    Pinmux_config(gCustomPinMux, PINMUX_DOMAIN_ID_MAIN); //Enable here
    int32_t status = SetupGenFWithSync0Out(gEnetAppCfg.hEnet);
    if (status != ENET_SOK)
    {
        EnetAppUtils_print("Failed to setup GENF with SYNC0_OUT\r\n");
    }
    //UpdateGenFFrequency(hEnet, 100000); /* Change to 100 kHz */
#endif    
}

void EnetApp_addMCastEntry(Enet_Type enetType,
                           uint32_t instId,
                           uint32_t coreId,
                           const uint8_t *testMCastAddr,
                           uint32_t portMask)
{
    Enet_IoctlPrms prms;
    int32_t status;
    CpswAle_SetMcastEntryInArgs setMcastInArgs;
    uint32_t setMcastOutArgs;

    if (Enet_isCpswFamily(enetType))
    {
        Enet_Handle hEnet = Enet_getHandle(enetType, instId);

        EnetAppUtils_assert(hEnet != NULL);
        memset(&setMcastInArgs, 0, sizeof(setMcastInArgs));
        memcpy(&setMcastInArgs.addr.addr[0U], testMCastAddr,
               sizeof(setMcastInArgs.addr.addr));
        setMcastInArgs.addr.vlanId  = 0;
        setMcastInArgs.info.super = false;
        setMcastInArgs.info.numIgnBits = 0;
        setMcastInArgs.info.fwdState = CPSW_ALE_FWDSTLVL_FWD;
        setMcastInArgs.info.portMask = portMask;
        ENET_IOCTL_SET_INOUT_ARGS(&prms, &setMcastInArgs, &setMcastOutArgs);
        ENET_IOCTL(hEnet, coreId, CPSW_ALE_IOCTL_ADD_MCAST, &prms, status);
        if (status != ENET_SOK)
        {
           EnetAppUtils_print("EnetTestBcastMcastLimit_AddAleEntry() failed CPSW_ALE_IOCTL_ADD_MCAST: %d\r\n",
                               status);
        }
    }
}

void EnetApp_addBroadcastEntry(void)
{
    EnetApp_addMCastEntry(gEnetAppCfg.enetType,
                          gEnetAppCfg.instId,
                          EnetSoc_getCoreId(),
                          BROADCAST_MAC_ADDRESS,
                          CPSW_ALE_ALL_PORTS_MASK);
}

void EnetApp_setMacAddr(uint8_t hwaddr[])
{
    memcpy(gEnetAppCfg.macAddr, hwaddr, ENET_MAC_ADDR_LEN);
    EnetAppUtils_print("Host MAC address Set: ");
    EnetAppUtils_printMacAddr(hwaddr);
}

static void EnetApp_portLinkStatusChangeCb(Enet_MacPort macPort,
                                           bool isLinkUp, void *appArg)
{
    EnetAppUtils_print("MAC Port %u: link %s\r\n",
                       ENET_MACPORT_ID(macPort), isLinkUp ? "up" : "down");
    cb_lld_notify_linkchange();
}

static void EnetApp_mdioLinkStatusChange(Cpsw_MdioLinkStateChangeInfo *info,
                                             void *appArg)
{
}

static void EnetApp_initAleConfig(CpswAle_Cfg *aleCfg)
{
    aleCfg->modeFlags = CPSW_ALE_CFG_MODULE_EN;
    aleCfg->agingCfg.autoAgingEn = true;
    aleCfg->agingCfg.agingPeriodInMs = 1000;

    aleCfg->nwSecCfg.vid0ModeEn = true;
    aleCfg->vlanCfg.unknownUnregMcastFloodMask = CPSW_ALE_ALL_PORTS_MASK;
    aleCfg->vlanCfg.unknownRegMcastFloodMask = CPSW_ALE_ALL_PORTS_MASK;
    aleCfg->vlanCfg.unknownVlanMemberListMask = CPSW_ALE_ALL_PORTS_MASK;
    aleCfg->policerGlobalCfg.policingEn = true;
    aleCfg->policerGlobalCfg.yellowDropEn = false;
    /* Enables the ALE to drop the red colored packets. */
    aleCfg->policerGlobalCfg.redDropEn = true;
    /* Policing match mode */
    aleCfg->policerGlobalCfg.policerNoMatchMode = CPSW_ALE_POLICER_NOMATCH_MODE_GREEN;
}

static void EnetApp_initEnetLinkCbPrms(Cpsw_Cfg *cpswCfg)
{
#if (ENET_SYSCFG_ENABLE_MDIO_MANUALMODE == 1U)
    cpswCfg->mdioLinkStateChangeCb = NULL;
    cpswCfg->mdioLinkStateChangeCbArg = NULL;
#else
    cpswCfg->mdioLinkStateChangeCb = EnetApp_mdioLinkStatusChange;
    cpswCfg->mdioLinkStateChangeCbArg = NULL;
#endif

    cpswCfg->portLinkStatusChangeCb    = &EnetApp_portLinkStatusChangeCb;
    cpswCfg->portLinkStatusChangeCbArg = NULL;
}

void EnetApp_updateCpswInitCfg(Enet_Type enetType, uint32_t instId, Cpsw_Cfg *cpswCfg)
{
    CpswHostPort_Cfg *hostPortCfg = &cpswCfg->hostPortCfg;
    /* Prepare init configuration for all peripherals */
    EnetAppUtils_print("\nInit all configs\r\n");
    EnetAppUtils_print("----------------------------------------------\r\n");
    EnetAppUtils_print("%s: init config\r\n", gEnetAppCfg.name);

    cpswCfg->hostPortCfg.removeCrc = true;
    cpswCfg->hostPortCfg.padShortPacket = true;
    cpswCfg->hostPortCfg.passCrcErrors = true;
    EnetApp_initEnetLinkCbPrms(cpswCfg);
    EnetApp_initAleConfig(&cpswCfg->aleCfg);

    /* Hardware switch priority is taken from packet's PCP or DSCP */
    hostPortCfg->rxVlanRemapEn     = true;
    hostPortCfg->rxDscpIPv4RemapEn = true;
    hostPortCfg->rxDscpIPv6RemapEn = true;

#if defined (SOC_AM263PX) || defined(SOC_AM263X) || defined(SOC_AM261X)
    EnetDma_Cfg *dmaCfg;
    /* Set the enChOverrideFlag to enable the channel override feature of CPDMA */
    dmaCfg=(EnetDma_Cfg *)cpswCfg->dmaCfg;
    dmaCfg->enChOverrideFlag = true;
#endif
}

static void EnetApp_closePort()
{
    Enet_IoctlPrms prms;
    Enet_MacPort macPort;
    uint32_t i;
    int32_t status;

    for (i = 0U; i < gEnetAppCfg.numMacPorts; i++)
    {
        macPort = gEnetAppCfg.macPorts[i];

        EnetAppUtils_print("%s: Close port %u\r\n", gEnetAppCfg.name, ENET_MACPORT_ID(macPort));

        /* Close port link */
        ENET_IOCTL_SET_IN_ARGS(&prms, &macPort);

        EnetAppUtils_print("%s: Close port %u link\r\n", gEnetAppCfg.name, ENET_MACPORT_ID(macPort));
        ENET_IOCTL(gEnetAppCfg.hEnet, gEnetAppCfg.coreId, ENET_PER_IOCTL_CLOSE_PORT_LINK, &prms, status);
        if (status != ENET_SOK)
        {
            EnetAppUtils_print("%s: Failed to close port link: %d\r\n", gEnetAppCfg.name, status);
        }
    }
}

void EnetApp_close()
{
    EnetAppUtils_print("\nClose Ports for all peripherals\r\n");
    EnetAppUtils_print("----------------------------------------------\r\n");
    EnetApp_closePort();

    /* Delete RX tasks created for all peripherals */
    EnetAppUtils_print("\nDelete RX tasks\r\n");
    EnetAppUtils_print("----------------------------------------------\r\n");
    EnetApp_destroyRxTask();

    /* Detach core */
    EnetAppUtils_print("\nDetach core from all peripherals\r\n");
    EnetAppUtils_print("----------------------------------------------\r\n");
    EnetApp_coreDetach(gEnetAppCfg.enetType,gEnetAppCfg.instId,
                       gEnetAppCfg.coreId,
                       gEnetAppCfg.coreKey);

    /* Close opened Enet drivers if any peripheral failed */
    EnetAppUtils_print("\nClose all peripherals\r\n");
    EnetAppUtils_print("----------------------------------------------\r\n");
    EnetApp_releaseHandleInfo(gEnetAppCfg.enetType, gEnetAppCfg.instId);
    gEnetAppCfg.hEnet = NULL;

    /* Do peripheral dependent initalization */
    EnetAppUtils_print("\nDeinit all peripheral clocks\r\n");
    EnetAppUtils_print("----------------------------------------------\r\n");
    EnetAppUtils_disableClocks(gEnetAppCfg.enetType, gEnetAppCfg.instId);
}
#ifdef TF_USE_GENF
#if 0
uint32_t my_count = 0;
void my_handler()
{
    my_count++;
}

void PrintMyCount()
{
    uint32_t currTime_ms = ClockP_getTimeUsec()/1000;

    DebugP_log("*** INT count: %lu,  %6d.%3d sec\r\n", my_count, currTime_ms/1000, currTime_ms%1000);
}

int32_t SetCptsGenf0ToSync0_Out()
{
    struct tisci_msg_rm_irq_set_req req = {0};
    struct tisci_msg_rm_irq_set_resp resp = {0};
    int32_t status;

    req.valid_params = TISCI_MSG_VALUE_RM_DST_ID_VALID |
                       TISCI_MSG_VALUE_RM_DST_HOST_IRQ_VALID;
    req.secondary_host = TISCI_MSG_VALUE_RM_UNUSED_SECONDARY_HOST;

    /* Hop 1: cpts_genf0 (1) -> TIMESYNC_EVENT_INTROUTER0 */
    req.src_id = TISCI_DEV_CPTS0;
    req.src_index = 1U;
    req.dst_id = TISCI_DEV_TIMESYNC_EVENT_INTROUTER0;
    req.dst_host_irq = 16U;     // cpts_genf0 ?
    status = Sciclient_rmIrqSet(&req, &resp, SystemP_WAIT_FOREVER);
    if (status != SystemP_SUCCESS)
    {
        return status;
    }

    /* Hop 2: TIMESYNC_EVENT_INTROUTER0 out 24 -> SYNC0_OUT */
    req.src_id = TISCI_DEV_TIMESYNC_EVENT_INTROUTER0;
    req.src_index = 1U;
    req.dst_id = TISCI_DEV_ECAP0;   // ECAP0_IN_APWM_OUT mode 1 => SYNC0_OUT ?
    req.dst_host_irq = 129U;     //CSLR_R5FSS0_CORE0_INTR_CPTS0_EVNT_PEND_0U;          // cpts_hw1_push ?
    status = Sciclient_rmIrqSet(&req, &resp, SystemP_WAIT_FOREVER);


    /* Set up ISR */
    HwiP_Params hwiPrms;
    HwiP_Object compHwiObject;
    HwiP_Params_init(&hwiPrms);
    hwiPrms.intNum   = 129U; //CSLR_R5FSS0_CORE0_INTR_CPTS0_EVNT_PEND_0U;
    hwiPrms.callback = my_handler;
    hwiPrms.args     = (void *)NULL;
    hwiPrms.isPulse     = 1;
    status = HwiP_construct(&compHwiObject, &hwiPrms);
    if(status != SystemP_SUCCESS)
    {
		DebugP_log("*** HwiP_construct() error\r\n");
    }

    return status;
}
#endif

#endif
