/*
 * ra2l-fsp.c
 *
 *  Created on: 13/04/2021
 *      Author: 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. *
  ------------------------------------------------------------------------
 */

#include <mcu-chip.h>


// this include provides access to
#include <mcu-conf.h>


/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_system.h"
#include "arm/arm_system.cch"


/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_gpio.h"
//#include <bsp_io.h>

/*******************************************************************************************************************//**
 * Writes to the specified pin's PFS register
 *
 * @param[in]    pin        Pin to write PFS data for
 * @param[in]    value      Value to be written to the PFS register
 *
 **********************************************************************************************************************/

#define IOPORT_PRV_PERIPHERAL_FUNCTION    (1U << 16)
/* Shift to get port in bsp_io_port_t and bsp_io_port_pin_t enums. */
#define IOPORT_PRV_PORT_OFFSET            (8U)

static inline
GPIOPinPFSReg* PortPFS(uint16_t pin){
    return (R_PFS_PORT_PIN_Type*) &( R_PFS->PORT[pin >> IOPORT_PRV_PORT_OFFSET].PIN[pin & BSP_IO_PRV_8BIT_MASK]);
}

static
void r_ioport_pfs_write (bsp_io_port_pin_t pin, uint32_t value)
{
    GPIOPinPFSReg* pfs = GPIO_PinPFS(pin);

    /* PMR bits should be cleared before specifying PSEL. Reference section "20.7 Notes on the PmnPFS Register Setting"
     * in the RA6M3 manual R01UH0886EJ0100. */
    if ((value & IOPORT_PRV_PERIPHERAL_FUNCTION) > 0)
    {
        /* Clear PMR */
        pfs->PmnPFS_b.PMR = 0;

        /* New config with PMR = 0 */
        pfs->PmnPFS = (value & ~((uint32_t) IOPORT_PRV_PERIPHERAL_FUNCTION));
    }

    /* Write configuration */
    pfs->PmnPFS = value;
}



void gpio_conf_func(const GPIOFUNC_INIT* x){
    r_ioport_pfs_write( x->pin, PINFUNC_PS(x->style_func) );
}


#if BSP_CFG_PFS_PROTECT
void gpio_conf_out(const PIN_INIT x){
    R_BSP_PinAccessEnable();           // Protect PWPR from re-entrancy
    GPIOPinPFSReg* pfs = GPIO_PinPFS(x.pin);
    pfs->PmnPFS = x.style;
    R_BSP_PinAccessDisable();           // Protect PWPR from re-entrancy
}

void gpio_conf_pin_style(uint16_t pin, unsigned style ){
    R_BSP_PinAccessEnable();           // Protect PWPR from re-entrancy
    GPIOPinPFSReg* pfs = GPIO_PinPFS(pin);
    pfs->PmnPFS = style;
    R_BSP_PinAccessDisable();           // Protect PWPR from re-entrancy
}
#endif



/// @brief config pins array <cfg>[count]
/// @arg count - <cfg> elements num
void gpio_confs(const PIN_INIT* cfg, unsigned count){
    R_BSP_PinAccessEnable();           // Protect PWPR from re-entrancy

    for( ; count > 0; --count, ++cfg)
        r_ioport_pfs_write( cfg->pin, cfg->style );

    R_BSP_PinAccessDisable();           // Protect PWPR from re-entrancy
}

/// @brief config pins array <cfg>[count] as periferial function pins
/// @arg count - <cfg> elements num
void gpio_confs_func(const GPIOFUNC_INIT* cfg, unsigned count){
    R_BSP_PinAccessEnable();           // Protect PWPR from re-entrancy

    for( ; count > 0; --count, ++cfg){
        uint16_t pf = PINFUNC_PF(cfg->style_func);
        if (pf == pfEND)
            return;
        uint32_t style = PINFUNC_PS(cfg->style_func);
        if (pf != pfNONE)
            style |= IOPORT_CFG_PERIPHERAL_PIN | (pf << IOPORT_PRV_PFS_PSEL_OFFSET) ;
        r_ioport_pfs_write( cfg->pin, style );
    }

    R_BSP_PinAccessDisable();           // Protect PWPR from re-entrancy
}


// \return - функция первого пина в pins
enum PinFuncID gpio_func(uint16_t pin){
    return GPIO_FUNC(pin);
}



/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_nvic.h"

void nvic_init(const NVIC_InitTypeDef x ) {
#ifdef NVIC_SetPriorityGrouping
    uint32_t grouping = NVIC_GetPriorityGrouping();
#else
#define grouping 0
#endif

    uint32_t prio = NVIC_EncodePriority(grouping, x.NVIC_IRQChannelPreemptionPriority
                                                , x.NVIC_IRQChannelSubPriority
                                        );
    NVIC_SetPriority((IRQn_Type)x.NVIC_IRQChannel, prio);
    if (x.NVIC_IRQChannelCmd != DISABLE){
        NVIC_EnableIRQ((IRQn_Type)x.NVIC_IRQChannel);
    }
    else
        NVIC_DisableIRQ((IRQn_Type)x.NVIC_IRQChannel);
}



/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_exti.h"

void EXTI_Init( NVIC_InitTypeDef irq, EXTILineID exti_line, uint32_t exti_options){
    int8_t irqn = irq.NVIC_IRQChannel;

    R_BSP_IrqClearPending( irqn );
    if (irq.NVIC_IRQChannelCmd == DISABLE)
        nvic_init(irq);

    // @sa r_icu.c

    /* IELSR Must be zero when modifying the IRQCR bits.
     * (See ICU Section 14.2.1 of the RA6M3 manual R01UH0886EJ0100). */
    uint32_t ielsr = R_ICU->IELSR[irqn];
    R_ICU->IELSR[irqn] = 0;

    /* Write IRQCR */
    R_ICU->IRQCR[exti_line] = exti_options;

    /* Restore IELSR. */
    R_ICU->IELSR[irqn] = ielsr;

    if (irq.NVIC_IRQChannelCmd != DISABLE)
        nvic_init(irq);
}



/////////////////////////////////////////////////////////////////////////////////////////
#if defined(MCUHAL_CONF_AGT)
#include "mcu_tim.h"
#include "r_agt.h"

void TIM_Init( TIM_TypeDef* TIMx, TIM_InitTypeDef mode){
    unsigned ch = TIM_idx(TIMx);

    /* Power on the AGT channel. */
    R_BSP_MODULE_START(FSP_IP_AGT, ch );
    /* Clear AGTCR. This stops the timer if it is running and clears the flags. */
    TIMx->AGTCR = AGT_STOP;

    /* Clear AGTMR2 before AGTMR1 is set. Reference Note 3 in section 25.2.6 "AGT Mode Register 2 (AGTMR2)"
     * of the RA6M3 manual R01UH0886EJ0100. */
    TIMx->AGTMR2 = 0U;

    /* Set count source and divider and configure pins. */
    TIMx->AGTMR1 = mode.mode;
    CSL_FINS( (TIMx->AGTMR2), R_AGT0_AGTMR2_CKS, mode.clockdiv );
}

void TIM_DeInit(TIM_TypeDef* TIMx){
    /* Stop timer */
    TIMx->AGTCR = AGT_STOP;

    /* Clear AGT output. */
    TIMx->AGTIOC = 0U;


    unsigned ch = TIM_idx(TIMx);
    /* Power on the AGT channel. */
    R_BSP_MODULE_STOP(FSP_IP_AGT, ch );
}

// TIMDevice API
void TIM_cfg_CLK(TIM_TypeDef* TIMx, AGTClkSrcID src, AGTPrescaleID prescale){

    //stop tim
    while (TIM_is_enabled(TIMx))
        TIM_disable(TIMx);

    CSL_FINS( (TIMx->AGTMR2), R_AGT0_AGTMR2_CKS, 0 );

    CSL_FINS( (TIMx->AGTMR1), R_AGT0_AGTMR1_TCK, src );
    CSL_FINS( (TIMx->AGTMR2), R_AGT0_AGTMR2_CKS, prescale );
}

#endif //defined(MCUHAL_CONF_AGT)


/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_rcc.h"

void RCC_EnableClock_SPIn( unsigned chanel ){
    R_BSP_MODULE_START(FSP_IP_SPI, chanel );
}

void RCC_DisableClock_SPIn( unsigned chanel ){
    R_BSP_MODULE_STOP(FSP_IP_SPI, chanel );
}

void RCC_EnableClock_UARTn( unsigned chanel ){
    R_BSP_MODULE_START(FSP_IP_SCI, chanel );
}

void RCC_DisableClock_UARTn( unsigned chanel ) {
    R_BSP_MODULE_STOP(FSP_IP_SCI, chanel );
}

void RCC_EnableClock_I2Cn( unsigned chanel ){
    R_BSP_MODULE_START(FSP_IP_IIC, chanel );
}

void RCC_DisableClock_I2Cn( unsigned chanel ) {
    R_BSP_MODULE_STOP(FSP_IP_IIC, chanel );
}

void RCC_EnableClock_ADCn( unsigned chanel ){
    R_BSP_MODULE_START(FSP_IP_ADC, chanel );
}

void RCC_DisableClock_ADCn( unsigned chanel ) {
    R_BSP_MODULE_STOP(FSP_IP_ADC, chanel );
}


void RCC_EnableClock_DMA( void ){
    R_BSP_MODULE_START(FSP_IP_DTC, 0 );
}

void RCC_DisableClock_DMA( void ) {
    R_BSP_MODULE_STOP(FSP_IP_DTC, 0 );
}

void RCC_EnableClock_TRNG(){
    R_BSP_MODULE_START(FSP_IP_TRNG, 0 );
}

void RCC_DisableClock_TRNG(){
    R_BSP_MODULE_STOP(FSP_IP_TRNG, 0 );
}
void RCC_EnableClock_AES(){
    R_BSP_MODULE_START(FSP_IP_AES, 0 );
}

void RCC_DisableClock_AES(){
    R_BSP_MODULE_STOP(FSP_IP_AES, 0 );
}



/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_usart.h"


int USART_set_baud(USART_TypeDef* uart, unsigned baud){
    unsigned uclk = RCC_GetUSARTClockFreq(uart);

    // TODO: adjust scale after 8/16 bitwidth choose ?
    uint8_t scale = uart->SMR_b.CKS *2;
    unsigned baud_fck = baud << scale;

    if ( baud_fck*6 > uclk )
        return FSP_ERR_UNDERFLOW;

    uint8_t save_cr = uart->SCR;
    /* Disables transmitter and receiver. This terminates any in-progress transmission. */
    uart->SCR = save_cr &  ~( USART_TE | USART_RE);

    if ( baud_fck > uclk/8 ) {
        uart->SEMR_b.ABCSE = 1;
        baud_fck = baud_fck * 6;
    }
    else if  ( baud_fck > uclk/16 ) {
        uart->SEMR_b.ABCSE  = 0;
        uart->SEMR_b.ABCS   = 1;
        baud_fck = baud_fck * 8;
    }
    else {
        uart->SEMR_b.ABCSE  = 0;
        uart->SEMR_b.ABCS   = 0;
        baud_fck = baud_fck * 16;
    }

    unsigned div = uclk / baud_fck;
    unsigned mod = uclk % baud_fck;

    if (div >= 0x200)
        return FSP_ERR_OVERFLOW;
    else if (div > 0x100)
    {
        uart->SEMR_b.BGDM   = 0;
        div = div /2;
        mod = mod /2;
        uclk = uclk / 2;
    }
    else
        uart->SEMR_b.BGDM   = 1;

    uart->BRR           = div-1;

    unsigned mddr = 0;
    if ( mod <= (uclk >> 8) ) {
        // division TOL enough
        uart->SEMR_b.BRME   = 0;

        uart->SCR = save_cr;
        return FSP_SUCCESS;
    }

    mddr = 255 - (mod << 8)/ uclk;
    uart->SEMR_b.BRME   = 1;
    uart->MDDR          = mddr;

    uart->SCR = save_cr;
    return FSP_SUCCESS;
}




/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_tim_gpt.h"

void GPT_enable( GPT_TypeDef* TIMx ){
    uint8_t ch = GPT_idx(TIMx);
    R_BSP_MODULE_START(FSP_IP_GPT, ch );
}

void GPT_disable( GPT_TypeDef* TIMx ){
    uint8_t ch = GPT_idx(TIMx);
    R_BSP_MODULE_STOP(FSP_IP_GPT, ch );
}

void GPT_Init( GPT_TypeDef* TIMx, GPT_InitTypeDef mode){
    unsigned ch = GPT_idx(TIMx);

    /* Power on the GPT channel. */
    GPT_enable( TIMx );
    GPT_protect( TIMx, DISABLE );

    // enable GPT_start/stop function
    TIMx->GTSSR |= R_GPT0_GTSSR_CSTRT_Msk;
    TIMx->GTPSR |= R_GPT0_GTSSR_CSTRT_Msk;
    TIMx->GTCSR |= R_GPT0_GTSSR_CSTRT_Msk;

    /* Clear AGTCR. This stops the timer if it is running and clears the flags. */
    //GPT_stop(TIMx);
    TIMx->GTSTP = 1 << ch;

    // disable all buffer operation
    TIMx->GTBER = 0;

    // setup count direction
    TIMx->GTUDDTYC = (((mode.mode >> GPT_MODE_CNT_DIR_Pos) & 1 ) << R_GPT0_GTUDDTYC_UD_Pos)
                    | R_GPT0_GTUDDTYC_UDF_Msk
                    ;

    TIMx->GTCR  = CSL_FMK(R_GPT0_GTCR_MD,   CSL_FEXT(mode.mode, GPT_MODE) )
                // FIX: FSP <= 2.4.0 have invalid R_GPT0_GTCR_TPCS_Pos
                //| CSL_FMK(R_GPT0_GTCR_TPCS, mode.clockdiv )
                | ((mode.clockdiv << 24) & R_GPT0_GTCR_TPCS_Msk)
                ;
}

void GPT_DeInit(GPT_TypeDef* TIMx){
    GPT_disable(TIMx);
}

#if BSP_CLK_STATIC

int32_t    us_to_gptticks(int32_t us, uint16_t scale){
    return US_TO_GPTTICKS(us, scale);
}

int32_t    gptticks_to_us(int32_t t, uint16_t scale){
    return GPTTICKS_TO_US(t, scale);
}

#else

#error "TODO: us_to_gptticks/gptticks_to_us"
uint32_t    us_to_gptticks(uint32_t us, uint16_t scale){
    //return US_TO_GPTTICKS(us, scale);
}

uint32_t    gptticks_to_us(uint32_t t, uint16_t scale){
    //return GPTTICKS_TO_US(t, scale);
}

#endif



/////////////////////////////////////////////////////////////////////////////////////////
#include "mcu_crc.h"

void CRC_enable(void){
    /* Power on CRC */
    R_BSP_MODULE_START(FSP_IP_CRC, 0);
}

void CRC_disable(void){
    /* Power off CRC */
    R_BSP_MODULE_STOP(FSP_IP_CRC, 0);
}


/////////////////////////////////////////////////////////////////////////////////////////
///
#include "mcu_pwr.h"

void    PWR_SetPowerMode (MCUPWRModeId PDMode){
    R_BSP_RegisterProtectDisable(BSP_REG_PROTECT_OM_LPC_BATT);
    R_SYSTEM->SBYCR_b.SSBY = PDMode;
    R_BSP_RegisterProtectEnable(BSP_REG_PROTECT_OM_LPC_BATT);
}

