/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

#ifndef _CLOCK_DRV_H_
#define _CLOCK_DRV_H_

/*! \brief Contains public interface to various functions related
 *         to the driver of Clock
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdint.h>
#include <stdbool.h>

/*! \addtogroup clock_drv
 *  @{
 */

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
 * the defines
 ******************************************************************************/

/* TCLK clock number */
#define TCLK_INPUTS_NUM (3U)

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*! \brief Definition of Clock names.
 */
typedef enum
{
    /* Source clocks */
    CLOCKDRV_SOSC = 0,     /* system crystal oscillator clock*/
    CLOCKDRV_SIRC,         /* slow internal reference clock */
    CLOCKDRV_PLL,          /* PLL clock */
    CLOCKDRV_SOSC_DIV,     /* SOSC_DIV clock */
    CLOCKDRV_SOSC_DIV_MFT, /* SOSC_DIV_MFT clock */
    CLOCKDRV_SIRC_DIV,     /* SIRC_DIV clock */
    CLOCKDRV_SIRC_DIV_MFT, /* SIRC_DIV_MFT clock */
    CLOCKDRV_PLL_DIV,      /* PLL_DIV clock */
    CLOCKDRV_PLL_DIV_MFT,  /* PLL_DIV_MFT clock */
    CLOCKDRV_SYS,          /* system clock */
    CLOCKDRV_CORE,         /* core clock */
    CLOCKDRV_APB,          /* bus clock */
    CLOCKDRV_FLS,          /* flash clock */
    CLOCKDRV_CLKOUT,       /* CLKOUT clock */
    CLOCKDRV_RTC_PADIN,    /* RTC pad input clock */
    CLOCKDRV_LPO_1K,       /* LPO 1KHz clock */
    CLOCKDRV_LPO_32K,      /* LPO 32KHz clock */
    CLOCKDRV_LPO_128K,     /* LPO 128KHz clock */
    /* Always-on clocks */
    CLOCKDRV_TOUCH,     /* TOUCH clock */
    CLOCKDRV_PORTA,     /* PORTA clock */
    CLOCKDRV_PORTB,     /* PORTB clock */
    CLOCKDRV_PORTC,     /* PORTC clock */
    CLOCKDRV_LPTMR,     /* LPTMR clock */
    CLOCKDRV_LPIT,      /* LPIT clock */
    CLOCKDRV_RTC_CNT,   /* RTC module clock for counter */
    CLOCKDRV_PORTA_FLT, /* PORTA digital filter clock */
    CLOCKDRV_PORTB_FLT, /* PORTB digital filter clock */
    CLOCKDRV_PORTC_FLT, /* PORTC digital filter clock */
    CLOCKDRV_EXT_FLT,   /* External Reset pin digital Filter clock */
    /* Peripheral clocks */
    CLOCKDRV_DMA,      /* DMA clock */
    CLOCKDRV_MSCM,     /* MSCM clock */
    CLOCKDRV_CRC,      /* CRC clock */
    CLOCKDRV_EIM,      /* EIM clock */
    CLOCKDRV_ERM,      /* ERM clock */
    CLOCKDRV_FHU,      /* FHU clock */
    CLOCKDRV_ADC,      /* ADC clock */
    CLOCKDRV_PDB,      /* PDB clock */
    CLOCKDRV_SPI0,     /* SPI0 clock */
    CLOCKDRV_SPI1,     /* SPI1 clock */
    CLOCKDRV_UART0,    /* UART0 clock */
    CLOCKDRV_UART1,    /* UART1 clock */
    CLOCKDRV_I2C,      /* I2C clock */
    CLOCKDRV_PWMLITE0, /* PWM_LITE0 clock */
    CLOCKDRV_PWMLITE1, /* PWM_LITE1 clock */
    CLOCKDRV_MFT0,     /* MFT0 input clock, please note this is not MFT functional clock */
    CLOCKDRV_MFT1,     /* MFT1 input clock, please note this is not MFT functional clock */
    CLOCKDRV_CMUS,     /* CMUS clock */
    CLOCKDRV_CMUP,     /* CMUP clock */
    CLOCKDRV_TCLK0,    /* TCLK0 clock */
    CLOCKDRV_TCLK1,    /* TCLK1 clock */
    CLOCKDRV_TCLK2,    /* TCLK2 clock */
    CLOCKDRV_EXT_MFT0, /* EXT_MFT0 clock */
    CLOCKDRV_EXT_MFT1, /* EXT_MFT1 clock */
    CLOCKDRV_FIX_MFT0, /* FIX_MFT0 clock */
    CLOCKDRV_FIX_MFT1, /* FIX_MFT1 clock */
    CLOCKDRV_MPU,      /* MPU clock */

    CLOCKDRV_CLOCK_NUM
} ClockDrv_NamesType;

/*! \brief module clock configuration.
 */
typedef struct _ClockDrv_ModuleClkConfigType_
{
    bool               gating; /*!< Clock gating. */
    ClockDrv_NamesType source; /*!< Clock source input (some modules don't have protocol clock) */
    uint16_t           mult;   /*!< Multiplier (some modules don't have fractional) */
    uint16_t           div;    /*!< Divider (some modules don't have divider) */

    /* Only PLL clock has following configures */
    struct
    {
        uint32_t outDiv;  /*!< out-divider */
        uint32_t preDiv;  /*!< pre-divider */
        uint32_t postDiv; /*!< post-divider */
    } pllConfig;          /*!< PLL related config */
} ClockDrv_ModuleClkConfigType;

/*! \brief Initial parameters
 */
typedef struct _ClockDrv_InitParamsType_
{
    uint32_t sircFreq;                  /*!< SIRC frequency */
    uint32_t xtalClkFreq;               /*!< XTAL clock frequency. */
    uint32_t rtcClkInFreq;              /*!< RTC_CLKIN clock frequency. */
    uint32_t tclkFreq[TCLK_INPUTS_NUM]; /*!< TCLKx clocks */
} ClockDrv_InitParamsType;

/* Forward declaration of related register */
struct _RcmRegType_;
struct _Pcc0RegType_;
struct _Scg0RegType_;
struct _Scg1RegType_;
struct _Pcc1RegType_;
struct _Scg2RegType_;

/*! \brief The definition of LPIT driver class
 */
typedef struct _ClockDrvType_
{
    struct _RcmRegType_  *rcmReg;                    /*!< pointer to RCM register */
    struct _Pcc0RegType_ *pcc0Reg;                   /*!< pointer to PCC0 register */
    struct _Scg0RegType_ *scg0Reg;                   /*!< pointer to SCG0 register */
    struct _Scg1RegType_ *scg1Reg;                   /*!< pointer to SCG1 register */
    struct _Pcc1RegType_ *pcc1Reg;                   /*!< pointer to PCC1 register */
    struct _Scg2RegType_ *scg2Reg;                   /*!< pointer to SCG2 register */
    uint32_t              sircFreq;                  /*!< SIRC frequency */
    uint32_t              xtalClkFreq;               /*!< XTAL clock frequency */
    uint32_t              rtcClkInFreq;              /*!< RTC_CLKIN clock frequency */
    uint32_t              tclkFreq[TCLK_INPUTS_NUM]; /*!< TCLKx clocks */
} ClockDrvType;

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the function prototypes
 ******************************************************************************/

/*! \brief Initializes the Clock driver module
 *
 * This function initializes Clock driver by setting the register
 * instances to it.
 *
 * \param[in] obj : pointer to Clock driver instance
 * \param[in] rcmReg : pointer to RCM register instance
 * \param[in] pcc0Reg : pointer to PCC0 register instance
 * \param[in] scg0Reg : pointer to SCG0 register instance
 * \param[in] scg1Reg : pointer to SCG1 register instance
 * \param[in] pcc1Reg : pointer to PCC1 register instance
 * \param[in] scg2Reg : pointer to SCG2 register instance
 * \param[in] initParams : pointer to clock initialization parameters
 */
extern void ClockDrv_Init(ClockDrvType                  *obj,
                          struct _RcmRegType_           *rcmReg,
                          struct _Pcc0RegType_          *pcc0Reg,
                          struct _Scg0RegType_          *scg0Reg,
                          struct _Scg1RegType_          *scg1Reg,
                          struct _Pcc1RegType_          *pcc1Reg,
                          struct _Scg2RegType_          *scg2Reg,
                          const ClockDrv_InitParamsType *initParams);

/*! \brief Gets the clock frequency for a specific clock name.
 *
 * This function checks the current clock configurations and then calculates
 * the clock frequency for a specific clock name defined in ClockDrv_NamesType.
 * Clock modules must be properly configured before using this function.
 * If it cannot find the clock name or the clock is not enabled or
 * it's not configured properly, it returns false.
 *
 * \note If return false, don't trust the value of out parameter "frequency"
 *       even if it's changed.
 *
 * \param[in] obj : pointer to Clock driver instance
 * \param[in] clockName : Clock names defined
 * \param[out] frequency : clock frequency value in Hertz
 * \return The result of the operation
 *         - false : Failed to get the frequency
 *         - true : Success
 */
extern bool ClockDrv_GetFreq(const ClockDrvType *obj, ClockDrv_NamesType clockName, uint32_t *frequency);

/*! \brief Configures module clock
 *
 * This function configures a module clock according to the configuration.
 *
 * \note If the target clock do not have mul or div, the corresponding configuration in
 *       moduleClkConfig will be ignored.
 *
 * \param[in] obj : pointer to Clock driver instance
 * \param[in] clockName : Clock name of the configured module clock
 * \param[in] moduleClkConfig : Pointer to the configuration structure.
 * \return The result of the operation
 *         - false : configure failed
 *         - true : success
 */
extern bool ClockDrv_ConfigureClock(ClockDrvType *obj, ClockDrv_NamesType clockName, const ClockDrv_ModuleClkConfigType *moduleClkConfig);

#ifdef __cplusplus
}
#endif /* extern "C" */

/*! @}*/

#endif /* _CLOCK_DRV_H_ */
