/*
|
* 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);
|
}
|