blob: 53fec3edc5d73ad48d3d85f6555416d1e3cf0130 [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation.
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/pm/policy.h>
#include <zephyr/spinlock.h>
#include <zephyr/sys_clock.h>
#include <zephyr/sys/time_units.h>
/** Lock to synchronize access to the events list. */
static struct k_spinlock events_lock;
/** List of events. */
static sys_slist_t events_list;
/** Pointer to Next Event. */
struct pm_policy_event *next_event;
/** @brief Update next event. */
static void update_next_event(uint32_t cyc)
{
int64_t new_next_event_cyc = -1;
struct pm_policy_event *evt;
/* unset the next event pointer */
next_event = NULL;
SYS_SLIST_FOR_EACH_CONTAINER(&events_list, evt, node) {
uint64_t cyc_evt = evt->value_cyc;
/*
* cyc value is a 32-bit rolling counter:
*
* |---------------->-----------------------|
* 0 cyc UINT32_MAX
*
* Values from [0, cyc) are events happening later than
* [cyc, UINT32_MAX], so pad [0, cyc) with UINT32_MAX + 1 to do
* the comparison.
*/
if (cyc_evt < cyc) {
cyc_evt += (uint64_t)UINT32_MAX + 1U;
}
if ((new_next_event_cyc < 0) || (cyc_evt < new_next_event_cyc)) {
new_next_event_cyc = cyc_evt;
next_event = evt;
}
}
}
int32_t pm_policy_next_event_ticks(void)
{
int32_t cyc_evt = -1;
if ((next_event) && (next_event->value_cyc > 0)) {
cyc_evt = next_event->value_cyc - k_cycle_get_32();
cyc_evt = MAX(0, cyc_evt);
BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CONFIG_SYS_CLOCK_TICKS_PER_SEC,
"HW Cycles per sec should be greater that ticks per sec");
return k_cyc_to_ticks_floor32(cyc_evt);
}
return -1;
}
void pm_policy_event_register(struct pm_policy_event *evt, uint32_t cycle)
{
k_spinlock_key_t key = k_spin_lock(&events_lock);
evt->value_cyc = cycle;
sys_slist_append(&events_list, &evt->node);
update_next_event(k_cycle_get_32());
k_spin_unlock(&events_lock, key);
}
void pm_policy_event_update(struct pm_policy_event *evt, uint32_t cycle)
{
k_spinlock_key_t key = k_spin_lock(&events_lock);
evt->value_cyc = cycle;
update_next_event(k_cycle_get_32());
k_spin_unlock(&events_lock, key);
}
void pm_policy_event_unregister(struct pm_policy_event *evt)
{
k_spinlock_key_t key = k_spin_lock(&events_lock);
(void)sys_slist_find_and_remove(&events_list, &evt->node);
update_next_event(k_cycle_get_32());
k_spin_unlock(&events_lock, key);
}