/**
 ****************************************************************************************
 *
 * @file ftdf.c
 *
 * @brief FTDF FreeRTOS Adapter
 *
 * Copyright (c) 2016, Dialog Semiconductor
 * 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. Neither the name of the copyright holder 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 HOLDER 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.
 *
 ****************************************************************************************
 */

#include <string.h>
#include <stdbool.h>
#ifndef FTDF_PHY_API
#include "osal.h"
#include "queue.h"

#include "sys_power_mgr.h"
#endif

#include "ad_rf.h"

#include "sys_tcs.h"

#include "black_orca.h"
#include "ad_ftdf.h"
#include "ad_rf.h"
#include "internal.h"
#include "regmap.h"
#if FTDF_DBG_BUS_ENABLE
#include "hw_gpio.h"
#endif /* FTDF_DBG_BUS_ENABLE */

#define WUP_LATENCY  (AD_FTDF_LP_CLOCK_CYCLE * AD_FTDF_WUP_LATENCY)

#ifdef FTDF_PHY_API
#ifndef PRIVILEGED_DATA
#define PRIVILEGED_DATA __attribute__((section("privileged_data_zi")))
#endif
#ifndef INITIALIZED_PRIVILEGED_DATA
#define INITIALIZED_PRIVILEGED_DATA __attribute__((section("privileged_data_rw")))
#endif
#endif

PRIVILEGED_DATA FTDF_Boolean explicit_sleep; /* = FTDF_FALSE; */
PRIVILEGED_DATA eSleepStatus sleep_status;

PRIVILEGED_DATA FTDF_ExtAddress uExtAddress;


static void ad_ftdf_sleep(void)
{

    FTDF_criticalVar();
    FTDF_enterCritical();
    REG_SET_BIT(CRG_TOP, PMU_CTRL_REG, FTDF_SLEEP);                 // go sleep
    FTDF_exitCritical();

    while (REG_GETF(CRG_TOP, SYS_STAT_REG, FTDF_IS_DOWN) == 0x0)    // don't go-go before I sleep
    {
    }
}

void ad_ftdf_wake_up_internal(bool sync)
{
    if (sleep_status != BLOCK_SLEEPING)
    {
        return;
    }

    FTDF_criticalVar();
    FTDF_enterCritical();
    /* Wake up power domains */
    REG_CLR_BIT(CRG_TOP, PMU_CTRL_REG, FTDF_SLEEP);                 // go wake up
    FTDF_exitCritical();

    while (REG_GETF(CRG_TOP, SYS_STAT_REG, FTDF_IS_UP) == 0x0)      // don't go-go before I sleep
    {
    }

    /* Power on and configure RF */
#if dg_configRF_ADAPTER
    ad_rf_request_on(false);
#else
    vPortEnterCritical();
    hw_rf_request_on(false);
    vPortExitCritical();
#endif

    /* Apply trim values */
    sys_tcs_apply(tcs_ftdf);

#if dg_configRF_ADAPTER
    ad_rf_request_recommended_settings();
#else
    vPortEnterCritical();
    hw_rf_request_recommended_settings();
    vPortExitCritical();
#endif

    if (sync)
    {
        FTDF_initLmac();
        sleep_status = BLOCK_ACTIVE;
    }
    else
    {
        /* Wake up FTDF block */
        sleep_status = BLOCK_WAKING_UP;
        FTDF_wakeUp();
#if defined(FTDF_NO_CSL) && defined(FTDF_NO_TSCH)
        ad_ftdf_wakeUpReady();
#endif
    }

}

void ad_ftdf_wake_up_async(void)
{
    ad_ftdf_wake_up_internal(false);
}

void ad_ftdf_wake_up_sync(void)
{
    ad_ftdf_wake_up_internal(true);
}

void sleep_when_possible(uint8_t explicit_request, uint32_t sleepTime)
{
    FTDF_Boolean blockSleep = FTDF_FALSE;

    if ((!AD_FTDF_SLEEP_WHEN_IDLE && !explicit_request) || sleep_status != BLOCK_ACTIVE)
    {
        return;
    }

    FTDF_USec us;


    FTDF_criticalVar();
    FTDF_enterCritical();
#ifdef FTDF_PHY_API

    if (explicit_request && FTDF_GET_FIELD(ON_OFF_REGMAP_LMACREADY4SLEEP) == 0)
    {
        volatile uint32_t *lmacCtrlMask = FTDF_GET_REG_ADDR(ON_OFF_REGMAP_LMAC_CONTROL_MASK);
        volatile uint32_t *lmacReady4sleepEvent = FTDF_GET_FIELD_ADDR(ON_OFF_REGMAP_LMACREADY4SLEEP_D);

        /* clear a previous interrupt */
        *lmacReady4sleepEvent = MSK_F_FTDF_ON_OFF_REGMAP_LMACREADY4SLEEP_D;

        /* Enable (unmask) interrupt */
        *lmacCtrlMask |= MSK_F_FTDF_ON_OFF_REGMAP_LMACREADY4SLEEP_M;
        us = 0;
    }
    else
    {
        us = FTDF_canSleep();
    }

#else
    us = FTDF_canSleep();
#endif

    /* Try to sleep as much as needed (if sleepTime is 0, then sleep as much as possible).
     * Otherwise, sleep as much as possible. */
    if (explicit_request == FTDF_TRUE)
    {
        if (sleepTime && us > sleepTime)
        {
            us = sleepTime;
        }
    }

    if (us > (WUP_LATENCY / 1000000) + AD_FTDF_SLEEP_COMPENSATION)
    {
        // Subtract sleep compensation from the sleep time, compensating for delays
        us -= AD_FTDF_SLEEP_COMPENSATION;

        blockSleep = FTDF_prepareForSleep(us);

        if (blockSleep)
        {
            // FTDF ready to sleep, disable clocks
            sleep_status = BLOCK_SLEEPING;
            explicit_sleep = explicit_request;
            ad_ftdf_sleep();
        }
    }

    FTDF_exitCritical();

    if (blockSleep)
    {
#if dg_configRF_ADAPTER
        ad_rf_request_off(false);
#else
        vPortEnterCritical();
        hw_rf_request_off(false);
        vPortExitCritical();
#endif
    }
}

/**
 * @brief Initialization function of FTDF adapter
 */
void ad_ftdf_init(void)
{
    /* Wake up power domains */
    REG_CLR_BIT(CRG_TOP, PMU_CTRL_REG, FTDF_SLEEP);                 // go wake up

    while (REG_GETF(CRG_TOP, SYS_STAT_REG, FTDF_IS_UP) == 0x0)      // don't go-go before I sleep
    {
    }

    REG_SET_BIT(CRG_TOP, CLK_RADIO_REG, FTDF_MAC_ENABLE);   // on
    REG_SETF(CRG_TOP, CLK_RADIO_REG, FTDF_MAC_DIV, 0);      // divide by 1

    /* Power on and configure RF */
    ad_rf_request_on(false);

    /* Apply trim values */
    sys_tcs_apply(tcs_ftdf);

    ad_rf_request_recommended_settings();

    FTDF_setSleepAttributes(AD_FTDF_LP_CLOCK_CYCLE,
                            AD_FTDF_WUP_LATENCY);

#ifdef FTDF_PHY_API
    ad_ftdf_init_phy_api();
#else
    ad_ftdf_init_mac_api();
#endif
}

void ad_ftdf_sleepCb(FTDF_USec sleepTime)
{
    sleep_when_possible(FTDF_TRUE, sleepTime);


}

#if FTDF_DBG_BUS_ENABLE
void ad_ftdf_dbgBusGpioConfig(void)
{
    hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_4, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_5, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_6, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_7, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_0, HW_GPIO_PIN_6, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_0, HW_GPIO_PIN_7, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_1, HW_GPIO_PIN_3, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
    hw_gpio_set_pin_function(HW_GPIO_PORT_2, HW_GPIO_PIN_3, HW_GPIO_MODE_OUTPUT,
                             HW_GPIO_FUNC_FTDF_DIAG);
}
#endif /* FTDF_DBG_BUS_ENABLE */
