Files
E3/e3_176_ref/drivers/source/btm/sdrv_btm.c
2025-10-21 19:40:27 +08:00

544 lines
13 KiB
C

/**
* @file sdrv_btm.c
*
* Copyright (c) 2021 Semidrive Semiconductor.
* All rights reserved.
*
* Description: Semidrive basic timer module driver.
*
* Revision History:
* -----------------
*/
#include <types.h>
#include <string.h>
#include "sdrv_btm.h"
#include "irq.h"
#include "bits.h"
#include "debug.h"
#include "regs_base.h"
#include "sdrv_ckgen.h"
#include "clock_ip.h"
#include "app/app_frm_signal.h"
#define SDRV_BTM_MODULE_NUM 6
sdrv_btm_controller_module_t btm_controller[SDRV_BTM_MODULE_NUM] = {0};
#define EXTRA_FMT "sdrv_btm:"
#define SDRV_BTM_ERR(format, ...) \
ssdk_printf(SSDK_ERR, EXTRA_FMT format, ##__VA_ARGS__)
#define SDRV_BTM_WARN(format, ...) \
ssdk_printf(SSDK_WARNING, EXTRA_FMT format, ##__VA_ARGS__)
#define SDRV_BTM_INFO(format, ...) \
ssdk_printf(SSDK_INFO, EXTRA_FMT format, ##__VA_ARGS__)
/**
* @brief sdrv btm irq handle function
*
* @param[in] irq :irq id
* @param[in] arg :irq arg
*/
static int sdrv_btm_irq_handle(uint32_t irq, void *arg)
{
sdrv_btm_controller_module_t *pstbtm_controller = arg;
sdrv_btm_t *sdrv_btm_g0 = pstbtm_controller->btm_bank[SDRV_BTM_G0];
sdrv_btm_t *sdrv_btm_g1 = pstbtm_controller->btm_bank[SDRV_BTM_G1];
volatile uint32_t int_sta = 0;
irq_state_t state = enter_critical_section();//20250426 增加中断保护
if (sdrv_btm_g0 != NULL) {
int_sta = sdrv_btm_hw_get_int_sta(sdrv_btm_g0->config->base);
}
else {
int_sta = sdrv_btm_hw_get_int_sta(sdrv_btm_g1->config->base);
}
uint32_t clr_int_sta;
if (int_sta & BTM_BM_INT_STAT_OE_G0) {
clr_int_sta = int_sta & BTM_BM_INT_STAT_OE_G0;
sdrv_btm_hw_set_int_sta(sdrv_btm_g0->config->base, clr_int_sta);
if (sdrv_btm_g0 != NULL) {
if (sdrv_btm_g0->callback) {
sdrv_btm_g0->callback(sdrv_btm_g0->arg);
}
/*update timer status*/
if (sdrv_btm_g0->type == BTM_TYPE_ONESHOT) {
sdrv_btm_g0->status = BTM_STATUS_IDLE;
}
}
}
if (int_sta & BTM_BM_INT_STAT_OE_G1) {
clr_int_sta = int_sta & BTM_BM_INT_STAT_OE_G1;
sdrv_btm_hw_set_int_sta(sdrv_btm_g1->config->base, clr_int_sta);
if (sdrv_btm_g1 != NULL) {
if (sdrv_btm_g1->callback) {
sdrv_btm_g1->callback(sdrv_btm_g1->arg);
}
/*update timer status*/
if (sdrv_btm_g1->type == BTM_TYPE_ONESHOT) {
sdrv_btm_g1->status = BTM_STATUS_IDLE;
}
}
}
exit_critical_section(state);//20250426 增加中断保护
return 0;
}
/**
* @brief get btm module id
*
* @param[in] base: module address
* @param[out] id : module id
*/
status_t sdrv_btm_get_module_id(uint32_t base, uint8_t *id)
{
status_t ret = SDRV_STATUS_FAIL;
switch (base) {
#if APB_BTM1_BASE
case APB_BTM1_BASE:
*id = 0;
ret = SDRV_STATUS_OK;
break;
#endif
#if APB_BTM2_BASE
case APB_BTM2_BASE:
*id = 1;
ret = SDRV_STATUS_OK;
break;
#endif
#if APB_BTM3_BASE
case APB_BTM3_BASE:
*id = 2;
ret = SDRV_STATUS_OK;
break;
#endif
#if APB_BTM4_BASE
case APB_BTM4_BASE:
*id = 3;
ret = SDRV_STATUS_OK;
break;
#endif
#if APB_BTM5_BASE
case APB_BTM5_BASE:
*id = 4;
ret = SDRV_STATUS_OK;
break;
#endif
#if APB_BTM6_BASE
case APB_BTM6_BASE:
*id = 5;
ret = SDRV_STATUS_OK;
break;
#endif
default:
*id = 0;
ret = SDRV_STATUS_FAIL;
break;
}
return ret;
}
/**
* @brief btm init
*
* Initial btm module instance.
*
* @param[in] ctrl: btm ctrl instance
* @param[in] cfg: btm config
* @return succeed:0 fail:other
*/
status_t sdrv_btm_init(sdrv_btm_t *ctrl, sdrv_btm_cfg_t *cfg)
{
uint8_t btm_id = 0u;
uint32_t apb_clock = 0u;
status_t status = 0;
ASSERT(ctrl != NULL);
ASSERT(cfg != NULL);
ctrl->config = cfg;
if (cfg->tmr_id == SDRV_BTM_G0) {
apb_clock = sdrv_ckgen_bus_get_rate(CLK_NODE(g_ckgen_bus_cr5_sf), CKGEN_BUS_CLK_OUT_P);
apb_clock = apb_clock / 1000;
ctrl->cnt_per_ms = (apb_clock * (cfg->tmr_cfg.inc_val)) / ((cfg->tmr_cfg.si_val) + 1);
}
else if (cfg->tmr_id == SDRV_BTM_G1) {
/* btm_g1 fre: 24M Hz */
ctrl->cnt_per_ms = (24000 * (cfg->tmr_cfg.inc_val)) / ((cfg->tmr_cfg.si_val) + 1);
}
else {
return SDRV_STATUS_INVALID_PARAM;
}
status = sdrv_btm_get_module_id(cfg->base, &btm_id);
if (status == SDRV_STATUS_FAIL)
return SDRV_STATUS_INVALID_PARAM;
sdrv_btm_hw_init(cfg->base, cfg->tmr_id, &(cfg->tmr_cfg));
ctrl->status = BTM_STATUS_IDLE;
ctrl->type = (btm_type_t)cfg->tmr_cfg.cnt_mode; /*oneshot or continous*/
ctrl->time_type = BTM_TIME_US;
ctrl->time_out = 0;
ctrl->callback = NULL;
btm_controller[btm_id].btm_bank[cfg->tmr_id] = ctrl;
if (cfg->irq > 0) {
irq_attach(cfg->irq, sdrv_btm_irq_handle, &btm_controller[btm_id]);
irq_enable(cfg->irq);
}
else {
return SDRV_STATUS_INVALID_PARAM;
}
return SDRV_STATUS_OK;
}
/**
* @brief btm deinit
*
* Deinitial btm module instance.
*
* @param[in] ctrl: btm ctrl instance
* @return succeed:0 fail:other
*/
status_t sdrv_btm_deinit(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
sdrv_btm_hw_timer_stop(ctrl->config->base, ctrl->config->tmr_id);
ctrl->status = BTM_STATUS_NULL;
ctrl->type = BTM_TYPE_ONESHOT;
ctrl->time_type = BTM_TIME_US;
ctrl->time_out = 0;
ctrl->callback = NULL;
ctrl->cnt_per_ms = 0;
return SDRV_STATUS_OK;
}
/**
* @brief btm start
*
* In one-short mode, btm will auto stop while reaches overflow value.
* In continuous mode, btm will reset to 0 and restart once reaches overflow value.
*
* @param[in] ctrl: btm ctrl instance
* @param[in] btm_type: timer type
* @param[in] time_type: timeout time unit
* @param[in] time_out: timeout
* @return succeed:0 fail:other
*/
status_t sdrv_btm_start(sdrv_btm_t *ctrl, btm_type_t btm_type, btm_time_type_t time_type,
uint32_t time_out)
{
uint32_t count = 0u;
ASSERT(ctrl != NULL);
if (BTM_STATUS_BUSY == ctrl->status) {
return SDRV_STATUS_BUSY;
}
ctrl->time_type = time_type;
ctrl->time_out = time_out;
switch (time_type) {
case BTM_TIME_MS: {
count = time_out * ctrl->cnt_per_ms;
}
break;
case BTM_TIME_US: {
count = (uint32_t)((uint64_t)time_out * ctrl->cnt_per_ms / 1000);
}
break;
case BTM_TIME_NS: {
count = (uint32_t)((uint64_t)time_out * ctrl->cnt_per_ms / 1000000);
}
break;
default: {
return SDRV_STATUS_INVALID_PARAM;
}
}
if (ctrl->type != btm_type) {
ctrl->type = btm_type;
ctrl->config->tmr_cfg.cnt_mode = (sdrv_btm_cnt_mode_e)btm_type;
sdrv_btm_hw_init(ctrl->config->base, ctrl->config->tmr_id, &(ctrl->config->tmr_cfg));
}
sdrv_btm_hw_timer_start(ctrl->config->base, ctrl->config->tmr_id, count - 1);
sdrv_btm_hw_compare_int_en(ctrl->config->base, ctrl->config->tmr_id, false);
ctrl->status = BTM_STATUS_BUSY;
return SDRV_STATUS_OK;
}
/**
* @brief btm stop
*
* BTM stop to count.
*
* @param[in] ctrl: btm ctrl instance
* @return succeed:0 fail:other
*/
status_t sdrv_btm_stop(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
sdrv_btm_hw_timer_stop(ctrl->config->base, ctrl->config->tmr_id);
ctrl->status = BTM_STATUS_IDLE;
return SDRV_STATUS_OK;
}
/**
* @brief btm setup callback funciton
*
* @param[in] ctrl: btm ctrl instance
* @param[in] callback: user callback function
* @return succeed:0 fail:other
*/
status_t sdrv_btm_set_callback(sdrv_btm_t *ctrl, btm_callback callback, void *arg)
{
ASSERT(ctrl != NULL);
ctrl->callback = callback;
ctrl->arg = arg;
return SDRV_STATUS_OK;
}
/**
* @brief get btm Elapsed time (us)
*
* @param[in] ctrl: btm ctrl instance
* @return btm Elapsed time (us)
*/
uint32_t sdrv_btm_get_current_time(sdrv_btm_t *ctrl)
{
uint32_t current = 0;
ASSERT(ctrl != NULL);
current = sdrv_btm_hw_get_cnt_val(ctrl->config->base, ctrl->config->tmr_id);
if (ctrl->cnt_per_ms == 0) {
return 0;
}
else {
return (uint32_t)((uint64_t)current * 1000 / ctrl->cnt_per_ms);
}
}
/**
* @brief get btm Elapsed time (extend)
*
* @param[in] ctrl: btm ctrl instance
* @param[in] time_type: time type
* @return btm Elapsed time (us)
*/
uint32_t sdrv_btm_get_current_time_ex(sdrv_btm_t *ctrl, btm_time_type_t time_type)
{
uint32_t current = 0;
ASSERT(ctrl != NULL);
current = sdrv_btm_hw_get_cnt_val(ctrl->config->base, ctrl->config->tmr_id);
switch (time_type) {
case BTM_TIME_MS: {
if (ctrl->cnt_per_ms == 0) {
return 0;
}
else {
return (uint32_t)(current / ctrl->cnt_per_ms);
}
}
break;
case BTM_TIME_US: {
if (ctrl->cnt_per_ms == 0) {
return 0;
}
else {
return (uint32_t)((uint64_t)current * 1000 / ctrl->cnt_per_ms);
}
}
break;
case BTM_TIME_NS: {
if (ctrl->cnt_per_ms == 0) {
return 0;
}
else {
return (uint32_t)((uint64_t)current * 1000000 / ctrl->cnt_per_ms);
}
}
break;
default: {
return 0;
}
}
}
/**
* @brief btm pause
*
* @param[in] ctrl: btm ctrl instance
* @return succeed:0 fail:other
*/
status_t sdrv_btm_pause(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
sdrv_btm_hw_cnt_stop(ctrl->config->base, ctrl->config->tmr_id, true);
ctrl->status = BTM_STATUS_IDLE;
return SDRV_STATUS_OK;
}
/**
* @brief btm continue
*
* @param[in] ctrl: btm ctrl instance
* @return succeed:0 fail:other
*/
status_t sdrv_btm_continue(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
sdrv_btm_hw_cnt_stop(ctrl->config->base, ctrl->config->tmr_id, false);
ctrl->status = BTM_STATUS_BUSY;
return SDRV_STATUS_OK;
}
/**
* @brief btm continue
*
* @param[in] ctrl: btm ctrl instance
* @return btm ctrl instance status
*/
btm_status_t sdrv_btm_get_status(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
return ctrl->status;
}
/**
* @brief get btm counter value
*
* @param[in] ctrl: btm ctrl instance
* @return btm counter value
*/
uint32_t sdrv_btm_get_counter(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
return sdrv_btm_hw_get_cnt_val(ctrl->config->base, ctrl->config->tmr_id);
}
/**
* @brief set btm overflow value
*
* @param[in] ctrl: btm ctrl instance
* @param[in] val: btm overflow value
* @return succeed:0 fail:other
*/
status_t sdrv_btm_set_overflow(sdrv_btm_t *ctrl, uint32_t val)
{
ASSERT(ctrl != NULL);
sdrv_btm_hw_set_ovf_cnt(ctrl->config->base, ctrl->config->tmr_id, val);
return SDRV_STATUS_OK;
}
/**
* @brief btm force reset counter value
*
* Btm force reset counter value to 0.
*
* @param[in] ctrl: btm ctrl instance
* @return succeed:0 fail:other
*/
status_t sdrv_btm_force_reload(sdrv_btm_t *ctrl)
{
ASSERT(ctrl != NULL);
sdrv_btm_hw_frc_rld(ctrl->config->base, ctrl->config->tmr_id);
return SDRV_STATUS_OK;
}
/**
* @brief is btm overflow.
*
* Check btm overflow status.
*
* @param[in] ctrl: btm ctrl instance
* @return true:overflow fail:not overflow
*/
bool sdrv_btm_is_overflowed(sdrv_btm_t *ctrl)
{
uint32_t int_sta = 0u;
ASSERT(ctrl != NULL);
int_sta = sdrv_btm_hw_get_int_sta(ctrl->config->base);
if (ctrl->config->tmr_id == SDRV_BTM_G1) {
return (int_sta & BTM_BM_INT_STAT_OE_G1) ? true : false;
}
else {
return (int_sta & BTM_BM_INT_STAT_OE_G0) ? true : false;
}
}
/**
* @brief btm clear interupt status
*
* @param[in] ctrl: btm ctrl instance
* @return succeed:0 fail:other
*/
status_t sdrv_btm_clear_intr(sdrv_btm_t *ctrl)
{
uint32_t int_sta = 0u;
ASSERT(ctrl != NULL);
int_sta = sdrv_btm_hw_get_int_sta(ctrl->config->base);
sdrv_btm_hw_set_int_sta(ctrl->config->base, int_sta);
return SDRV_STATUS_OK;
}
/**
* @brief btm convert count to time in us
*
* @param[in] ctrl: btm ctrl instance
* @param[in] cnt: btm count value
* @return time in microseconds.
*/
uint32_t sdrv_btm_count_to_us(sdrv_btm_t *ctrl, uint32_t cnt)
{
ASSERT(ctrl != NULL);
return (ctrl->cnt_per_ms) ? (uint32_t)((uint64_t)cnt * 1000 / ctrl->cnt_per_ms) : 0;
}