/******************************************************************************
版权所有：    @copyright (C) 2024-2034 HaiYang Technology Corp. All rights reserved.
文件名称：    ustimer.c
文件版本：    01.01
创建作者:     zhaoyang
创建日期：    2025-07-28
功能说明：    us定时器模块驱动程序。目前使用了TIM2定时器，作为延时函数的参考定时器用。
              TIM2的时钟周期为1/3us。延时函数的最大时间长度为1431.6557653秒。
其它说明：
修改记录：
*/
#include "bsp_ustimer.h"
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include "bsp_ccu.h"
#include "rt_types.h"

#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
#define BSP_USTIMER_DELAY_MAX_US	(10UL*60*BSP_USTIMER_SEC) //10分钟

int g_ustimer_fd = -1;
unsigned g_ustimer_mapped_size;
void *g_ustimer_map_base, *g_ustimer_virt_addr; 

/******************************************************************************
函数名称：    bsp_ustimer_init
函数版本：    01.01
创建作者：    zhaoyang
创建日期：    2025-07-28
函数说明：    使用timer2做为延时函数参考定时器并注册
参数说明：    无
返回值：      无
修改记录：
*/
int bsp_ustimer_init(void)
{   
    void *temp_ccu_base, *temp_virt_l_base, *temp_virt_h_base, *temp_virt_ctl_base;
    unsigned long read_result;
    off_t target;
	unsigned page_size, offset_in_page;
    unsigned width = 8 * sizeof(int);
    target = TIMER_BASE;
    // 获取页面大小
    g_ustimer_mapped_size = page_size = sysconf(_SC_PAGESIZE);
    offset_in_page = (unsigned)target & (page_size - 1);
    if (offset_in_page + width > page_size) {
		/* This access spans pages.
		 * Must map two pages to make it possible: */
		g_ustimer_mapped_size *= 2;
	}
    
    g_ustimer_fd = open("/dev/mem",  O_RDWR | O_SYNC);
    if (g_ustimer_fd < 0)  
    {
        printf("open(/dev/mem) failed.\n");    
        return -1;
    }
    fflush(stdout);

    g_ustimer_map_base = mmap (NULL, g_ustimer_mapped_size, PROT_READ | PROT_WRITE, MAP_SHARED, g_ustimer_fd, target & ~(off_t)(page_size - 1));
    if (g_ustimer_map_base == (void *)-1)
    {
        printf ("NULL pointer!\n");
    }
    else
    {
        printf ("BSP ustimer map Successfull!\n");
    }
    fflush(stdout);

    g_ustimer_virt_addr = (char*)g_ustimer_map_base + offset_in_page;
    // printf("g_ustimer_virt_addr: %p %p %x\n", g_ustimer_virt_addr, g_ustimer_map_base, offset_in_page);

    // Init timer ccu clock
    /* set TMR2 clock source to HOSC, 8 pre-division */
    temp_ccu_base = g_ccu_map_base + 0x0808;
    read_result = *(volatile u_int32_t*)(temp_ccu_base);
    read_result &= ~((0x01 << 24) | 7);
    read_result |= (1 << 31) | 3;
    *(volatile u_int32_t*)(temp_ccu_base) = read_result;

    // Enable timer2
    /* set timer intervalue */
    temp_virt_l_base = (char*)g_ustimer_map_base + 0x64;
    temp_virt_h_base = (char*)g_ustimer_map_base + 0x6C;
    *(volatile u_int32_t*)(temp_virt_l_base) = 0xFFFFFFFF;
    *(volatile u_int32_t*)(temp_virt_h_base) = 0xFFFFFF;

	/* set mode to auto reload */
    temp_virt_ctl_base = (char*)g_ustimer_map_base + 0x60;
	read_result = *(volatile u_int32_t*)(temp_virt_ctl_base);
    read_result &= TIMER_CTL_PERIODIC;
    *(volatile u_int32_t*)(temp_virt_ctl_base) = (read_result | TIMER_CTL_AUTORELOAD | TIMER_CTL_ENABLE);
    // read_result =  *(volatile u_int32_t*)g_ustimer_virt_addr;

    // printf("ustimer_init res: 0x%08lx\n", read_result);

    return 0;
}

/******************************************************************************
函数名称：    bsp_ustimer_exit
函数版本：    01.01
创建作者：    zhaoyang
创建日期：    2025-07-28
函数说明：    用户定时器注销
参数说明：    无
返回值：      无
修改记录：
*/
int bsp_ustimer_exit(void)
{
    if (g_ustimer_fd >= 0)
    {
        close(g_ustimer_fd);
        g_ustimer_fd = -1;
    }

    if (munmap(g_ustimer_map_base, g_ustimer_mapped_size) == -1) {
        printf("munmap failed!");
        return -1;
    }
   
    return 0;
}

/******************************************************************************
函数名称：    bsp_ustimer_get_origin
函数版本：    01.01
创建作者：    zhaoyang
创建日期：    2025-07-28
函数说明：    得到当前时刻定时器的值，用作ustimer_get_duration、
              ustimer_delay_origin函数的origin参数。
参数说明：    无
返回值：      当前时刻定时器的值。
修改记录：
*/
unsigned long bsp_ustimer_get_origin(void)
{
    void *temp_virt_l_base, *temp_virt_h_base;
    u_int64_t val_low, val_high;
    if (!g_ustimer_map_base) {
        return 0;
    }
    temp_virt_l_base = (char*)g_ustimer_map_base + 0x68;
    temp_virt_h_base = (char*)g_ustimer_map_base + 0x70;

    val_low = (~*(volatile u_int32_t*)temp_virt_l_base) & TIMER_VL_MASK;

    val_high = (~*(volatile u_int32_t*)temp_virt_h_base) & TIMER_VH_MASK;

    return ((val_high << TIMER_VH_OFFSET) | val_low)/3;
}

/******************************************************************************
函数名称：    bsp_ustimer_get_duration
函数版本：    01.01
创建作者：    zhaoyang
创建日期：    2025-07-28
函数说明：    得到当前时刻相对于指定时间原点的时间间隔，以us为单位。
参数说明：    origin:    时间原点，以us为单位。
返回值：      当前时刻相对于原点的的时间间隔。
修改记录：
*/
unsigned long bsp_ustimer_get_duration(unsigned long origin)
{
    if(!g_ustimer_map_base)
		return 0;
    // 返回差值(根据C语言算法，会自动处理回绕问题)
    return bsp_ustimer_get_origin() - origin;
}

/******************************************************************************
函数名称：    bsp_ustimer_delay_origin
函数版本：    01.01
创建作者：    zhaoyang
创建日期：    2025-07-28
函数说明：    以us为单位的延时函数。最大延时值为1小时（3600000000us）和
             ustimer_delay函数不同的是bsp_ustimer_delay_origin函数的延时计算的时间
             原点为origin，而不是函数被调用的时刻。当这个函数退出时，会确保延时
             大于等于设定值。如果需要高精度的延时，调用这个函数时应先屏蔽中断。
参数说明：
    origin:  延时计算的时间原点，以us为单位，通常通过ustimer_get_origin函数获得。
    us：     延时us数。
返回值：
    0：        成功
    其它：    失败
修改记录：
*/
unsigned long bsp_ustimer_delay_origin(unsigned long origin,unsigned long us)
{
    if(!g_ustimer_map_base)
		return 0;

    //检查参数
	if(us > BSP_USTIMER_DELAY_MAX_US)
		return -1;

    // 循环等待
    while (us > (bsp_ustimer_get_origin() - origin))
        ;
    return 0;
}

/******************************************************************************
函数名称：    ustimer_delay
函数版本：    01.01
创建作者：    zhaoyang
创建日期：    2025-07-28
函数说明：    以us为单位的延时函数。最大延时值为1小时（3600000000us）。
            当这个函数退出时，会确保延时大于等于设定值。如果需要高精度的延时，
            调用这个函数时应先屏蔽中断。
参数说明：
    us：    延时us数。
返回值：
    0：    成功
    其它：    失败
修改记录：
*/
unsigned long bsp_ustimer_delay(unsigned long us)
{
    u64 origin;

    if(!g_ustimer_map_base)
		return 0;

    //检查参数
	if(us > BSP_USTIMER_DELAY_MAX_US)
		return -1;

    origin = bsp_ustimer_get_origin();
	bsp_ustimer_delay_origin(origin, us);
    return 0;
}
