/* * Copyright (c) 2019-2025 Beijing Hanwei Innovation Technology Ltd. Co. and * its subsidiaries and affiliates (collectly called MKSEMI). * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into an MKSEMI * integrated circuit in a product or a software update for such product, * 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. * * 3. Neither the name of MKSEMI nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * 4. This software, with or without modification, must only be used with a * MKSEMI integrated circuit. * * 5. Any software provided in binary form under this license must not be * reverse engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL MKSEMI OR 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 "mk_list.h" #include "mk_timer_list.h" #include "mk_misc.h" #include "mk_rtc.h" #include "mk_power.h" #define DEBUG_EN 0 #if DEBUG_EN #include "mk_trace.h" #endif #define MIN_TURNOVER_TICK_CNT (10) static uint32_t ref_pt; static mk_list_t meta_list; static void mk_timer_init(drv_callback_t callback); static uint32_t mk_timer_get_count(void); extern void mk_timer_start(uint32_t ticks); static uint32_t _past_ticks(uint32_t now) { return (now >= ref_pt) ? now - ref_pt : UINT32_MAX - ref_pt + now; } static void _insert_timer(mk_timer_t *p, uint32_t ticks, uint8_t mode) { mk_timer_t *p_last = NULL; uint32_t lock = int_lock(); /* if timer is already running, stop and renew it */ if (p->active) { mk_timer_list_stop_timer(p); } p->active = true; p->ticks = ticks; p->count = ticks; p->mode = mode; mk_timer_t *p_curr = (mk_timer_t *)meta_list.first; while (p_curr != NULL) { if (p->ticks < p_curr->ticks) { break; } p_last = p_curr; p_curr = p_curr->p_next; } if (p_last != NULL) mk_list_insert_after(&meta_list, (mk_list_hdr_t *)p_last, (mk_list_hdr_t *)p); else mk_list_push_front(&meta_list, (mk_list_hdr_t *)p); int_unlock(lock); } static void _post_process_timer(mk_timer_t *p) { if (p->mode == MK_TIMER_PERIODIC) { _insert_timer(p, p->count, MK_TIMER_PERIODIC); } else { mk_timer_list_stop_timer(p); } } static void mk_timer_list_update(uint32_t now) { uint32_t lock = int_lock(); uint32_t ticks = _past_ticks(now); mk_timer_t *p = (mk_timer_t *)meta_list.first; while (p != NULL) { p->ticks = (p->ticks > ticks) ? p->ticks - ticks : 0; /* timer expired */ if (p->ticks < POWER_DOWN_TIME_TICK_MIN) { p->ticks = 0; if (p->fast_cb == NULL) { #ifdef WSF_EN WsfTaskSetReady(p->handlerId, WSF_TIMER_EVENT); #else // mk_event_set(); #endif } } #if DEBUG_EN LOG_INFO(TRACE_MODULE_OS, "mk_timer_list_update %u %u %u %u\r\n", p->handlerId, p->msg.event, p->ticks, ticks); #endif p = p->p_next; } bool wip = true; while (wip) { p = (mk_timer_t *)meta_list.first; if (p == NULL) break; /* iterate over timer queue */ while (p != NULL) { if ((p->ticks == 0) && (p->fast_cb != NULL)) { p->fast_cb(); _post_process_timer(p); wip = true; break; } p = p->p_next; wip = false; } } ref_pt = now; int_unlock(lock); } void mk_timer_list_start_timer(mk_timer_t *p, uint32_t ms, uint8_t mode) { uint32_t lock = int_lock(); uint32_t now = mk_timer_get_count(); mk_timer_list_update(now); _insert_timer(p, __MS_TO_32K_CNT(ms), mode); uint32_t pd_tick = mk_timer_list_tick_left(); if (pd_tick != UINT32_MAX) { mk_timer_start(pd_tick); } int_unlock(lock); } void mk_timer_list_stop_timer(mk_timer_t *p) { uint32_t lock = int_lock(); mk_list_extract(&meta_list, (mk_list_hdr_t *)p); p->active = false; int_unlock(lock); } mk_timer_t *mk_timer_list_pick_expired(void) { uint32_t lock = int_lock(); mk_timer_t *p = (mk_timer_t *)meta_list.first; if ((p != NULL) && (p->ticks == 0)) { _post_process_timer(p); } else { p = NULL; } int_unlock(lock); return p; } uint32_t mk_timer_list_tick_left(void) { uint32_t ticks; uint32_t lock = int_lock(); if (meta_list.first == NULL) { ticks = UINT32_MAX; } else { ticks = ((mk_timer_t *)meta_list.first)->ticks; uint32_t elapsed_ticks = _past_ticks(mk_timer_get_count()); ticks = (ticks > elapsed_ticks) ? ticks - elapsed_ticks : 0; } int_unlock(lock); return ticks; } static void mk_timer_callback(void *dev, uint32_t now) { uint32_t lock = int_lock(); mk_timer_list_update(now); uint32_t pd_tick = mk_timer_list_tick_left(); if (pd_tick != UINT32_MAX) { mk_timer_start(pd_tick); } int_unlock(lock); } void mk_timer_list_init(void) { mk_list_init(&meta_list); mk_timer_init(mk_timer_callback); } /** * @brief Initialize the low power timer. * * @param[in] callback The callback function of low power timer. */ static void mk_timer_init(drv_callback_t callback) { power_wakeup_enable(POWER_WAKEUP_BY_RTC_ALARM, POWER_WAKEUP_LEVEL_NONE); // enable rtc alarm interrupt and register callback rtc_set_alarm(RTC_ID0, rtc_get(RTC_ID0) - 1U, callback); } /** * @brief Get low power timer count. * * @return Low power timer count. */ static uint32_t mk_timer_get_count(void) { return rtc_get(RTC_ID0); } /** * @brief Start hardware low power timer. * * @param[in] ticks The number of ticks to wakeup from power down mode. */ void mk_timer_start(uint32_t ticks) { ticks = (ticks > MIN_TURNOVER_TICK_CNT) ? ticks : MIN_TURNOVER_TICK_CNT; ticks += rtc_get(RTC_ID0); rtc_set_alarm_lite(RTC_ID0, ticks); }