/*
 * mcu_tim_gpt.h
 *
 *  Created on: 23/04/2021.
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   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 ARM 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 COPYRIGHT HOLDERS AND 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. *
  ------------------------------------------------------------------------
    this is for STM SPL compatibily wraper for GPT timer devices
 */

#ifndef CPU_RENESAS_RA2L_FSP_MCU_GPT_H_
#define CPU_RENESAS_RA2L_FSP_MCU_GPT_H_

#include <stdint.h>
#include <mcu-chip.h>

#if !defined(RA_H)
#warning "uncknown HAL, expects renesas RA"
#endif

#include <c_compat.h>
#include "cslr.h"

#include "mcu_rcc.h"
//#include "r_timer_api.h"
// provide GPTn timers constants
//#include "r_gpt.h"


typedef R_GPT0_Type     GPT_TypeDef;

#define GPT_REG_SIZE    ((uint32_t) R_GPT1 - (uint32_t) R_GPT0)
#define GPT_idx( gpt )   ( ( ((uint32_t)(gpt)) - R_GPT0_BASE )/GPT_REG_SIZE)
#define GPTn(n)       ( (GPT_TypeDef *)( R_GPT0_BASE + ((n)*GPT_REG_SIZE) ) )


#define _GPT_CAT2(a,b)    a##b
#define _GPT_CAT3(a,b,c)  a##b##c

/// @brief FSP configurator generates this IRQn_Type ID in vector_data.h
/// @arg n - digit 0..1
//#define GPTn(n)        _GPT_CAT2(R_GPT,n)
#define GPTn_IRQ_OVER(n)    _GPT_CAT3(GPT, n, _COUNTER_OVERFLOW_IRQn)
#define GPTn_IRQ_ZERO(n)    _GPT_CAT3(GPT, n, _COUNTER_UNDERFLOW_IRQn)
#define GPTn_CMPA_IRQ(n)    _GPT_CAT3(AGT, n, _COMPARE_A_IRQn)
#define GPTn_CMPB_IRQ(n)    _GPT_CAT3(AGT, n, _COMPARE_B_IRQn)
#define GPTn_CMPC_IRQ(n)    _GPT_CAT3(AGT, n, _COMPARE_C_IRQn)
#define GPTn_CMPD_IRQ(n)    _GPT_CAT3(AGT, n, _COMPARE_D_IRQn)
#define GPTn_CMPE_IRQ(n)    _GPT_CAT3(AGT, n, _COMPARE_E_IRQn)
#define GPTn_CMPF_IRQ(n)    _GPT_CAT3(AGT, n, _COMPARE_F_IRQn)

//-----------------------------------------------------------------
// this is port of through all HALs
#define GPT_STATUS(TIMx)        (TIMx)->GTST
#define GPT_VALUE(TIMx)         (TIMx)->GTCNT


#ifdef __cplusplus
extern "C" {
#endif

typedef enum GPTChID{
    GPT_OC1 = 0, GPT_OCA = GPT_OC1,
    GPT_OC2 = 1, GPT_OCB = GPT_OC2,
    GPT_OC3 = 2, GPT_OCC = GPT_OC3,
    GPT_OC4 = 3, GPT_OCD = GPT_OC4,
    GPT_OC5 = 4, GPT_OCE = GPT_OC5,
    GPT_OC6 = 5, GPT_OCF = GPT_OC6,
} GPTChID;

INLINE
void GPT_SetCHCompare(GPT_TypeDef* TIMx, GPTChID ch, uint32_t x){
    TIMx->GTCCR[ch] = x;
}




enum GPTStatusFlag{
    GPT_IF_OCA              = R_GPT0_GTST_TCFA_Msk,
    GPT_IF_OCB              = R_GPT0_GTST_TCFB_Msk,
    GPT_IF_OCC              = R_GPT0_GTST_TCFC_Msk,
    GPT_IF_OCD              = R_GPT0_GTST_TCFD_Msk,
    GPT_F_OCE               = R_GPT0_GTST_TCFE_Msk,
    GPT_F_OCF               = R_GPT0_GTST_TCFF_Msk,

    GPT_IF_ZERO             = R_GPT0_GTST_TCFPU_Msk,
    GPT_IF_OVER             = R_GPT0_GTST_TCFPO_Msk,

    GPT_DIRF                = R_GPT0_GTST_TUCF_Msk,
    GPT_ODF                 = R_GPT0_GTST_ODF_Msk,      //< Output Disable Flag

    GPT_IF_OCx              = (GPT_IF_OCA | GPT_IF_OCA| GPT_IF_OCC | GPT_IF_OCD) ,
    GPT_IF_OCx_Pos          = R_GPT0_GTST_TCFA_Pos ,

    GPT_IF_RELOAD           = GPT_IF_OVER,

    GPT_IF_ALL              = GPT_IF_OCx | GPT_IF_ZERO | GPT_IF_OVER,
};

#define GPT_IF(TIMx)        GPT_STATUS(TIMx)

static inline
void GPT_IF_clear( GPT_TypeDef* TIMx, uint8_t mask ){
    GPT_STATUS(TIMx) = GPT_STATUS(TIMx)& ~(mask & GPT_IF_ALL);
}


enum GPTCtrlFlag {
    GPT_START               = R_GPT0_GTCR_CST_Msk ,
    GPT_ENA                 = GPT_START,

};


static inline
void GPT_protect( GPT_TypeDef* TIMx , bool en){
    enum {GPT_KEY = 0xA5 << 8,};
    TIMx->GTWP = GPT_KEY | (en & 1);
}

static inline
void GPT_start( GPT_TypeDef* TIMx ){
    TIMx->GTSTR = 1 << GPT_idx(TIMx);
}

static inline
void GPT_stop( GPT_TypeDef* TIMx ){
    TIMx->GTSTP = 1 << GPT_idx(TIMx);
}

static inline
void GPT_clear( GPT_TypeDef* TIMx ){
    TIMx->GTCLR = 1 << GPT_idx(TIMx);
}

void GPT_enable( GPT_TypeDef* TIMx );
void GPT_disable( GPT_TypeDef* TIMx );

/*
static inline
bool GPT_is_enabled(TIM_TypeDef* TIMx){
    return (TIM_STATUS(TIMx) & AGT_CSTF) != 0;
}
*/


INLINE
void GPT_SetAutoReload(GPT_TypeDef* TIMx, uint32_t x) {
    TIMx->GTPR = x;
}



typedef enum GTPModeID{
    GPT_MODE_FASTPWM    = 0,    //< Saw-wave PWM mode (single buffer or double buffer possible)
    GPT_MODE_PULSE      = 1,    //< Saw-wave one-shot pulse mode (fixed buffer operation)
    //< Triangle-wave PWM mode 1 (32-bit transfer at trough) (single buffer or double buffer is possible)
    GPT_MODE_PWM1       = 4,
    GPT_MODE_PWM2       = 5,
    GPT_MODE_PWM3       = 6,

    GPT_MODE            = R_GPT0_GTCR_MD_Msk,
    GPT_MODE_Msk        = R_GPT0_GTCR_MD_Msk,
    GPT_MODE_Pos        = R_GPT0_GTCR_MD_Pos,

    GPT_CR_Msk          = GPT_MODE_Msk,         //< // ControlReg mask

    // flags valid only for GPT_Init
    GPT_MODE_CNT_DN     = 0,
    GPT_MODE_CNT_UP     = 0x80,
    GPT_MODE_CNT_DIR    = 0x80,
    GPT_MODE_CNT_DIR_Pos= 7,
} GPTModeID;

typedef enum GPTPrescaleID {
    GPTPrescale1    = 0,
    GPTPrescale4    = 1,
    GPTPrescale16   = 2,
    GPTPrescale64   = 3,
    GPTPrescale256  = 4,
    GPTPrescale1024 = 5,

    GPTPrescaleTOP  = GPTPrescale1024,

    GPTPrescale     = R_GPT0_GTCR_TPCS_Msk,
    GPTPrescale_Msk = R_GPT0_GTCR_TPCS_Msk,
    GPTPrescale_Pos = R_GPT0_GTCR_TPCS_Pos,
} GPTPrescaleID;

static inline
unsigned GPTScale(GPTPrescaleID x){
    return 1<<(x*2);
}



struct GPT_InitTypeDef {
    uint8_t     mode;           //< GPTModeID
    uint8_t     clockdiv;       //< GPTPrescaleID
};
typedef struct GPT_InitTypeDef GPT_InitTypeDef;

// STM LL HAL wraps
void GPT_Init( GPT_TypeDef* TIMx, GPT_InitTypeDef mode);
void GPT_DeInit(GPT_TypeDef* TIMx);


typedef enum GPTSourceFlag {
    GPTSRC_ETRA_RISE        = 1,    //<on the rising edge of GTETRGA input
    GPTSRC_ETRA_FALL        = 2,    //<on the falling edge of GTETRGA input
    GPTSRC_ETRB_RISE        = 4,    //<on the rising edge of GTETRGB input
    GPTSRC_ETRB_FALL        = 8,    //<on the falling edge of GTETRGB input

    GPTSRC_IOCn_ARBL        = 0x100,//<rising edge of GTIOCnA input when GTIOCnB input is 0
    GPTSRC_IOCn_ARBH        = 0x200,//<rising edge of GTIOCnA input when GTIOCnB input is 1
    GPTSRC_IOCn_AFBL        = 0x400,//<falling edge of GTIOCnA input when GTIOCnB input is 0
    GPTSRC_IOCn_AFBH        = 0x800,//<falling edge of GTIOCnA input when GTIOCnB input is 1
    GPTSRC_IOCn_ALBR        = 0x1000,//<rising edge of GTIOCnB input when GTIOCnA input is 0
    GPTSRC_IOCn_AHBR        = 0x2000,//<rising edge of GTIOCnB input when GTIOCnA input is 1
    GPTSRC_IOCn_ALBF        = 0x4000,//<falling edge of GTIOCnB input when GTIOCnA input is 0
    GPTSRC_IOCn_AHBF        = 0x8000,//<falling edge of GTIOCnB input when GTIOCnA input is 1

    GPTSRC_ELC_GPTA         = 0x10000, //<at the ELC_GPTA input
    GPTSRC_ELC_GPTB         = 0x20000, //<at the ELC_GPTB input
    GPTSRC_ELC_GPTC         = 0x40000, //<at the ELC_GPTC input
    GPTSRC_ELC_GPTD         = 0x80000, //<at the ELC_GPTD input

    GPTSRC_SW               = 0x80000000ul, //<d by the GTSTP/GTSTR register
} GPTSourceFlag;
typedef uint32_t    GPTSources;

static inline
void GPT_starts_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTSSR = x;
}

static inline
void GPT_stops_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTPSR = x;
}

static inline
void GPT_clears_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTCSR = x;
}

static inline
void GPT_cntup_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTUPSR = x;
}

static inline
void GPT_cntdn_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTDNSR = x;
}

static inline
void GPT_capA_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTICASR = x;
}

static inline
void GPT_capB_set( GPT_TypeDef* TIMx , GPTSources x){
    TIMx->GTICBSR = x;
}





int32_t    us_to_gptticks(int32_t us, uint16_t scale);
int32_t    gptticks_to_us(int32_t t, uint16_t scale);

#if BSP_CLK_STATIC
/*---------------------------------------------------------------------------*/
#define GPT_SECOND             BSP_PCLKD_HZ

//* this scale used for achieve maximum arguments range, avoiding overload on int32 value
#define GPTRT_USCALC_SCALE     8u
#define GPTRT_US               1000000ul
#define GPTRT_SCALED_US        (GPTRT_US/GPTRT_USCALC_SCALE)
#define GPTRT_SCALED_SECOND    (GPT_SECOND/GPTRT_USCALC_SCALE)
#if ( (GPTRT_SCALED_SECOND*GPTRT_USCALC_SCALE) != (GPT_SECOND) )
#error please fix GPTRT_USCALC_SCALE selection for your GPT_SECOND!!!
#endif

#define US_TO_GPTTICKS(US, scale)  ( ((US) >= 0) ?                        \
                               ( ((uint32_t)(US) * (GPTRT_SCALED_SECOND/scale) + (GPTRT_SCALED_US/2u) ) / GPTRT_SCALED_US ) :      \
                                -( ((uint32_t)(-US) * (GPTRT_SCALED_SECOND/scale) + (GPTRT_SCALED_US/2u) ) / GPTRT_SCALED_US )\
                                )

#define GPTTICKS_TO_US(T,scale)   (((T) >= 0) ?                     \
                               (((uint32_t)(T) * (GPTRT_SCALED_US/scale) + (GPTRT_SCALED_SECOND/2u) ) / GPTRT_SCALED_SECOND ) : \
                               -(((uint32_t)(-T) * (GPTRT_SCALED_US/scale) + (GPTRT_SCALED_SECOND/2u) ) / GPTRT_SCALED_SECOND )\
                               )

#else

#define GPT_SECOND                  RCC_GetGPTClockFreq( GPTn(0) )
#define US_TO_GPTTICKS(US, scale)   us_to_gptticks(US, scale)
#define GPTTICKS_TO_US(T,scale)     gptticks_to_us(T, scale)

#endif


#ifdef __cplusplus
}
#endif


#endif /* CPU_RENESAS_RA2L_FSP_MCU_TIM_H_ */
