/** * @file sdrv_btm.c * * Copyright (c) 2021 Semidrive Semiconductor. * All rights reserved. * * Description: Semidrive basic timer module driver. * * Revision History: * ----------------- */ #include #include #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; }