/*
 * Copyright 2019-2023 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_smartdma.h"

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.lpc_smartdma"
#endif

/*******************************************************************************
 * Definitions
 ******************************************************************************/

typedef void (*smartdma_func_t)(void);

#define SMARTDMA_HANDSHAKE_EVENT  0U
#define SMARTDMA_HANDSHAKE_ENABLE 1U
#define SMARTDMA_MASK_RESP        2U
#define SMARTDMA_ENABLE_AHBBUF    3U
#define SMARTDMA_ENABLE_GPISYNCH  4U

#if defined(SMARTDMA0) && !(defined(SMARTDMA))
#define SMARTDMA SMARTDMA0
#endif

/*******************************************************************************
 * Variables
 ******************************************************************************/
static smartdma_func_t *s_smartdmaApiTable;
static smartdma_callback_t s_smartdmaCallback;
static void *s_smartdmaCallbackParam;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Codes
 ******************************************************************************/
/*!
 * brief Initialize the SMARTDMA.
 *
 * param apiMemAddr The address firmware will be copied to.
 * param firmware The firmware to use.
 * param firmwareSizeByte Size of firmware.
 */
void SMARTDMA_Init(uint32_t apiMemAddr, const void *firmware, uint32_t firmwareSizeByte)
{
    SMARTDMA_InitWithoutFirmware();
    SMARTDMA_InstallFirmware(apiMemAddr, firmware, firmwareSizeByte);
}

/*!
 * brief Initialize the SMARTDMA.
 *
 * This function is similar with SMARTDMA_Init, the difference is this function
 * does not install the firmware, the firmware could be installed using
 * SMARTDMA_InstallFirmware.
 */
void SMARTDMA_InitWithoutFirmware(void)
{
    /* Clear Smart DMA RAM */
    RESET_PeripheralReset(kSMART_DMA_RST_SHIFT_RSTn);
    CLOCK_EnableClock(kCLOCK_Smartdma);
}

/*!
 * @brief Install the firmware.
 *
 * param apiMemAddr The address firmware will be copied to.
 * param firmware The firmware to use.
 * param firmwareSizeByte Size of firmware.
 */
void SMARTDMA_InstallFirmware(uint32_t apiMemAddr, const void *firmware, uint32_t firmwareSizeByte)
{
    (void)memcpy((void *)(uint8_t *)apiMemAddr, firmware, firmwareSizeByte);
    SMARTDMA->CTRL     = (0xC0DE0000U | (1U << SMARTDMA_ENABLE_GPISYNCH));
    s_smartdmaApiTable = (smartdma_func_t *)apiMemAddr;
}

/*!
 * brief Install the complete callback function..
 *
 * param callback The callback called when smartdma program finished.
 */
void SMARTDMA_InstallCallback(smartdma_callback_t callback, void *param)
{
    s_smartdmaCallback      = callback;
    s_smartdmaCallbackParam = param;
}

/*!
 * brief Boot the SMARTDMA to run program.
 *
 * param apiIndex Index of the API to call.
 * param pParam Pointer to the parameter.
 * param mask Value set to SMARTDMA_ARM2SMARTDMA[0:1].
 */
void SMARTDMA_Boot(uint32_t apiIndex, void *pParam, uint8_t mask)
{
    SMARTDMA->ARM2EZH = (uint32_t)(uint8_t *)pParam | (uint32_t)mask;
    SMARTDMA->BOOTADR = (uint32_t)(s_smartdmaApiTable[apiIndex]);
    SMARTDMA->CTRL    = 0xC0DE0011U | (0U << SMARTDMA_MASK_RESP) | (0U << SMARTDMA_ENABLE_AHBBUF); /* BOOT */
};

/*!
 * brief Boot the SMARTDMA to run program.
 *
 * param the API to call.
 * param pParam Pointer to the parameter.
 * param mask Value set to SMARTDMA_ARM2SMARTDMA[0:1].
 */
void SMARTDMA_Boot_API(uint32_t apiAddress, void *pParam, uint8_t mask)
{
    SMARTDMA->ARM2EZH = (uint32_t)(uint8_t *)pParam | (uint32_t)mask;
    SMARTDMA->BOOTADR = apiAddress;
    SMARTDMA->CTRL    = 0xC0DE0011U | (0U << SMARTDMA_MASK_RESP) | (0U << SMARTDMA_ENABLE_AHBBUF); /* BOOT */
};

/*!
 * brief Deinitialize the SMARTDMA.
 */
void SMARTDMA_Deinit(void)
{
    SMARTDMA->CTRL = 0xC0DE0000U;
    CLOCK_DisableClock(kCLOCK_Smartdma);
}

/*!
 * brief Reset the SMARTDMA.
 */
void SMARTDMA_Reset(void)
{
    RESET_PeripheralReset(kSMART_DMA_RST_SHIFT_RSTn);
    SMARTDMA->CTRL = (0xC0DE0000U | (1U << SMARTDMA_ENABLE_GPISYNCH));
}

/*!
 * brief SMARTDMA IRQ.
 */
void SMARTDMA_HandleIRQ(void)
{
    if (NULL != s_smartdmaCallback)
    {
        s_smartdmaCallback(s_smartdmaCallbackParam);
    }
}

/*!
 * brief SMARTDMA Set EX flag.
 */
void SMARTDMA_SetExternalFlag(uint8_t flag)
{
    volatile uint32_t smartdma_ctrl = (SMARTDMA->CTRL & 0x0000FFFF);
    if (flag == 0) {
    	smartdma_ctrl &= ~(1 << 1);
    } else {
    	smartdma_ctrl |= (1 << 1);
    }
    SMARTDMA->CTRL = (0xC0DE0000 | smartdma_ctrl);
}

/*!
 * brief SMARTDMA Access RAM.
 */
void SMARTDMA_AccessShareRAM(uint8_t flag){
	SMARTDMA_SetExternalFlag(flag);
}
