/********************************** (C) COPYRIGHT *******************************
 * File Name          : CH59x_SYS.c
 * Author             : WCH
 * Version            : V1.2
 * Date               : 2021/11/17
 * Description
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

#include "CH59x_common.h"

/*********************************************************************
 * @fn      SetSysClock
 *
 * @brief   配置系统运行时钟
 *
 * @param   sc      - 系统时钟源选择 refer to SYS_CLKTypeDef
 *
 * @return  none
 */
__HIGH_CODE
void SetSysClock(SYS_CLKTypeDef sc)
{
    sys_safe_access_enable();
    R8_PLL_CONFIG &= ~(1 << 5); //
    sys_safe_access_disable();
    if (sc & 0x20) // HSE div
    {
        sys_safe_access_enable();
        R32_CLK_SYS_CFG = (0 << 6) | (sc & 0x1f) | RB_TX_32M_PWR_EN | RB_PLL_PWR_EN;
        __nop();
        __nop();
        __nop();
        __nop();
        sys_safe_access_disable();
        sys_safe_access_enable();
        SAFEOPERATE;
        R8_FLASH_CFG = 0X51;
        sys_safe_access_disable();
    }

    else if (sc & 0x40) // PLL div
    {
        sys_safe_access_enable();
        R32_CLK_SYS_CFG = (1 << 6) | (sc & 0x1f) | RB_TX_32M_PWR_EN | RB_PLL_PWR_EN;
        __nop();
        __nop();
        __nop();
        __nop();
        sys_safe_access_disable();
        sys_safe_access_enable();
        R8_FLASH_CFG = 0X52;
        sys_safe_access_disable();
    }
    else
    {
        sys_safe_access_enable();
        R32_CLK_SYS_CFG |= RB_CLK_SYS_MOD;
        sys_safe_access_disable();
    }
    // 更改FLASH clk的驱动能力
    sys_safe_access_enable();
    R8_PLL_CONFIG |= 1 << 7;
    sys_safe_access_disable();
}

/*********************************************************************
 * @fn      GetSysClock
 *
 * @brief   获取当前系统时钟
 *
 * @param   none
 *
 * @return  Hz
 */
uint32_t GetSysClock(void)
{
    uint16_t rev;

    rev = R32_CLK_SYS_CFG & 0xff;
    if ((rev & 0x40) == (0 << 6))
    { // 32M进行分频
        return (32000000 / (rev & 0x1f));
    }
    else if ((rev & RB_CLK_SYS_MOD) == (1 << 6))
    { // PLL进行分频
        return (480000000 / (rev & 0x1f));
    }
    else
    { // 32K做主频
        return (32000);
    }
}

/*********************************************************************
 * @fn      SYS_GetInfoSta
 *
 * @brief   获取当前系统信息状态
 *
 * @param   i       - refer to SYS_InfoStaTypeDef
 *
 * @return  是否开启
 */
uint8_t SYS_GetInfoSta(SYS_InfoStaTypeDef i)
{
    if (i == STA_SAFEACC_ACT)
    {
        return (R8_SAFE_ACCESS_SIG & RB_SAFE_ACC_ACT);
    }
    else
    {
        return (R8_GLOB_CFG_INFO & (1 << i));
    }
}

/*********************************************************************
 * @fn      SYS_ResetExecute
 *
 * @brief   执行系统软件复位
 *
 * @param   none
 *
 * @return  none
 */
__HIGH_CODE
void SYS_ResetExecute(void)
{
    FLASH_ROM_SW_RESET();
    sys_safe_access_enable();
    R8_RST_WDOG_CTRL |= RB_SOFTWARE_RESET;
    sys_safe_access_disable();
}

/*********************************************************************
 * @fn      SYS_DisableAllIrq
 *
 * @brief   关闭所有中断，并保留当前中断值
 *
 * @param   pirqv   - 当前保留中断值
 *
 * @return  none
 */
void SYS_DisableAllIrq(uint32_t *pirqv)
{
    *pirqv        = (PFIC->ISR[0] >> 8) | (PFIC->ISR[1] << 24);
    PFIC->IRER[0] = 0xffffffff;
    PFIC->IRER[1] = 0xffffffff;
}

/*********************************************************************
 * @fn      SYS_RecoverIrq
 *
 * @brief   恢复之前关闭的中断值
 *
 * @param   irq_status  - 当前保留中断值
 *
 * @return  none
 */
void SYS_RecoverIrq(uint32_t irq_status)
{
    PFIC->IENR[0] = (irq_status << 8);
    PFIC->IENR[1] = (irq_status >> 24);
}

/*********************************************************************
 * @fn      SYS_GetSysTickCnt
 *
 * @brief   获取当前系统(SYSTICK)计数值
 *
 * @param   none
 *
 * @return  当前计数值
 */
uint32_t SYS_GetSysTickCnt(void)
{
    uint32_t val;

    val = SysTick->CNT;
    return (val);
}

/*********************************************************************
 * @fn      WWDG_ITCfg
 *
 * @brief   看门狗定时器溢出中断使能
 *
 * @param   s       - 溢出是否中断
 *
 * @return  none
 */
void WWDG_ITCfg(FunctionalState s)
{
    uint8_t ctrl = R8_RST_WDOG_CTRL;

    if (s == DISABLE)
    {
        ctrl &= ~RB_WDOG_INT_EN;
    }
    else
    {
        ctrl |= RB_WDOG_INT_EN;
    }

    sys_safe_access_enable();
    R8_RST_WDOG_CTRL = ctrl;
    sys_safe_access_disable();
}

/*********************************************************************
 * @fn      WWDG_ResetCfg
 *
 * @brief   看门狗定时器复位功能
 *
 * @param   s       - 溢出是否复位
 *
 * @return  none
 */
void WWDG_ResetCfg(FunctionalState s)
{
    uint8_t ctrl = R8_RST_WDOG_CTRL;

    if (s == DISABLE)
    {
        ctrl &= ~RB_WDOG_RST_EN;
    }
    else
    {
        ctrl |= RB_WDOG_RST_EN;
    }

    sys_safe_access_enable();
    R8_RST_WDOG_CTRL = ctrl;
    sys_safe_access_disable();
}

/*********************************************************************
 * @fn      WWDG_ClearFlag
 *
 * @brief   清除看门狗中断标志，重新加载计数值也可清除
 *
 * @param   none
 *
 * @return  none
 */
void WWDG_ClearFlag(void)
{
    sys_safe_access_enable();
    R8_RST_WDOG_CTRL |= RB_WDOG_INT_FLAG;
    sys_safe_access_disable();
}

/*********************************************************************
 * @fn      HardFault_Handler
 *
 * @brief   硬件错误中断，进入后执行复位，复位类型为上电复位
 *
 * @param   none
 *
 * @return  none
 */
__INTERRUPT
__HIGH_CODE
__attribute__((weak)) void HardFault_Handler(void)
{
    // https://www.cnblogs.com/gscw/p/18159920
    // 监控函数和 PC 指针可以同时使用。监控函数需要在蓝牙工程中使用才有意义，如果打印值为空，检查是否使用蓝牙。
    struct __MEMORY_CTL
    {
        struct __MEMORY_CTL *pNext;
        uint16_t len;
        uint16_t used;
    };
    typedef struct __MEMORY_CTL MemoryCtl;

    extern MemoryCtl *MemCtlStart;
    extern MemoryCtl *MemCtlEnd;
    MemoryCtl *MemHead;
    MemHead = MemCtlStart;
    while (MemHead != MemCtlEnd)
    {
        printf("|%8x,%8x,%8d.....\n", MemHead->used, (uint32_t)MemHead,
               (uint32_t)(MemHead->pNext) - (uint32_t)MemHead - sizeof(struct __MEMORY_CTL));
        MemHead = MemHead->pNext;
    }
    printf("\n");

    uint32_t v_mepc, v_mcause, v_mtval;
    printf("hardfault\n");

    v_mepc   = __get_MEPC();
    v_mcause = __get_MCAUSE();
    v_mtval  = __get_MTVAL();

    printf("mepc:%08x\n", v_mepc);
    printf("mcause:%08x\n", v_mcause);
    printf("mtval:%08x\n", v_mtval);
#if 0
    FLASH_ROM_SW_RESET();
    sys_safe_access_enable();
    R16_INT32K_TUNE = 0xFFFF;
    sys_safe_access_disable();
    sys_safe_access_enable();
    R8_RST_WDOG_CTRL |= RB_SOFTWARE_RESET;
    sys_safe_access_disable();
#endif
    while (1);
}

/*********************************************************************
 * @fn      mDelayuS
 *
 * @brief   uS 延时
 *
 * @param   t       - 时间参数
 *
 * @return  none
 */
__HIGH_CODE
void mDelayuS(uint16_t t)
{
    uint32_t i;
#if (FREQ_SYS == 80000000)
    i = t * 20;
#elif (FREQ_SYS == 60000000)
    i = t * 15;
#elif (FREQ_SYS == 48000000)
    i = t * 12;
#elif (FREQ_SYS == 40000000)
    i = t * 10;
#elif (FREQ_SYS == 32000000)
    i = t << 3;
#elif (FREQ_SYS == 24000000)
    i = t * 6;
#elif (FREQ_SYS == 16000000)
    i = t << 2;
#elif (FREQ_SYS == 8000000)
    i = t << 1;
#elif (FREQ_SYS == 4000000)
    i = t;
#elif (FREQ_SYS == 2000000)
    i = t >> 1;
#elif (FREQ_SYS == 1000000)
    i = t >> 2;
#else
    i = t << 1;
#endif
    do
    {
        __nop();
    } while (--i);
}

/*********************************************************************
 * @fn      mDelaymS
 *
 * @brief   mS 延时
 *
 * @param   t       - 时间参数
 *
 * @return  none
 */
__HIGH_CODE
void mDelaymS(uint16_t t)
{
    uint16_t i;

    for (i = 0; i < t; i++)
    {
        mDelayuS(1000);
    }
}

#ifdef DEBUG
int _write(int fd, char *buf, int size)
{
    int i;
    for (i = 0; i < size; i++)
    {
#if DEBUG == Debug_UART0
        while (R8_UART0_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
        R8_UART0_THR = *buf++;                  /* 发送数据 */
#elif DEBUG == Debug_UART1
        while (R8_UART1_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
        R8_UART1_THR = *buf++;                  /* 发送数据 */
#elif DEBUG == Debug_UART2
        while (R8_UART2_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
        R8_UART2_THR = *buf++;                  /* 发送数据 */
#elif DEBUG == Debug_UART3
        while (R8_UART3_TFC == UART_FIFO_SIZE); /* 等待数据发送 */
        R8_UART3_THR = *buf++;                  /* 发送数据 */
#endif
    }
    return size;
}

#endif

// // https://www.cnblogs.com/debugdabiaoge/p/18543792
// __HIGH_CODE
// void *__wrap_memcpy(void *dst ,void *src, size_t size)
// {
//     __MCPY(dst, src, (void *)((uint32_t)src+size));
//     return dst;
// }

/*********************************************************************
 * @fn      _sbrk
 *
 * @brief   Change the spatial position of data segment.
 *
 * @return  size: Data length
 */
__attribute__((used)) void *_sbrk(ptrdiff_t incr)
{
    extern char _end[];
    extern char _heap_end[];
    static char *curbrk = _end;

    if ((curbrk + incr < _end) || (curbrk + incr > _heap_end))
        return NULL - 1;

    curbrk += incr;
    return curbrk - incr;
}
