2022-09-02 19:57:48 +08:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
|
|
/*
|
|
|
|
* A general-purpose cyclic execution infrastructure, to allow "small"
|
|
|
|
* (run-time wise) functions to be executed at a specified frequency.
|
|
|
|
* Things like LED blinking or watchdog triggering are examples for such
|
|
|
|
* tasks.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2022 Stefan Roese <sr@denx.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __cyclic_h
|
|
|
|
#define __cyclic_h
|
|
|
|
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <asm/types.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct cyclic_info - Information about cyclic execution function
|
|
|
|
*
|
|
|
|
* @func: Function to call periodically
|
|
|
|
* @ctx: Context pointer to get passed to this function
|
|
|
|
* @name: Name of the cyclic function, e.g. shown in the commands
|
|
|
|
* @delay_ns: Delay is ns after which this function shall get executed
|
|
|
|
* @start_time_us: Start time in us, when this function started its execution
|
|
|
|
* @cpu_time_us: Total CPU time of this function
|
|
|
|
* @run_cnt: Counter of executions occurances
|
|
|
|
* @next_call: Next time in us, when the function shall be executed again
|
|
|
|
* @list: List node
|
2022-10-17 15:00:58 +08:00
|
|
|
* @already_warned: Flag that we've warned about exceeding CPU time usage
|
2022-09-02 19:57:48 +08:00
|
|
|
*/
|
|
|
|
struct cyclic_info {
|
|
|
|
void (*func)(void *ctx);
|
|
|
|
void *ctx;
|
|
|
|
char *name;
|
|
|
|
uint64_t delay_us;
|
|
|
|
uint64_t start_time_us;
|
|
|
|
uint64_t cpu_time_us;
|
|
|
|
uint64_t run_cnt;
|
|
|
|
uint64_t next_call;
|
cyclic: switch to using hlist instead of list
A hlist is headed by just a single pointer, so can only be traversed
forwards, and insertions can only happen at the head (or before/after
an existing list member). But each list node still consists of two
pointers, so arbitrary elements can still be removed in O(1).
This is precisely what we need for the cyclic_list - we never need to
traverse it backwards, and the order the callbacks appear in the list
should really not matter.
One advantage, and the main reason for doing this switch, is that an
empty list is represented by a NULL head pointer, so unlike a
list_head, it does not need separate C code to initialize - a
memset(,0,) of the containing structure is sufficient.
This is mostly mechanical:
- The iterators are updated with an h prefix, and the type of the
temporary variable changed to struct hlist_node*.
- Adding/removing is now just hlist_add_head (and not tail) and
hlist_del().
- struct members and function return values updated.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Stefan Roese <sr@denx.de>
Tested-by: Stefan Roese <sr@denx.de>
Tested-by: Tim Harvey <tharvey@gateworks.com> # imx8mm-venice-*
2022-10-28 19:50:53 +08:00
|
|
|
struct hlist_node list;
|
2022-10-17 15:00:58 +08:00
|
|
|
bool already_warned;
|
2022-09-02 19:57:48 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/** Function type for cyclic functions */
|
|
|
|
typedef void (*cyclic_func_t)(void *ctx);
|
|
|
|
|
|
|
|
#if defined(CONFIG_CYCLIC)
|
|
|
|
/**
|
|
|
|
* cyclic_register - Register a new cyclic function
|
|
|
|
*
|
|
|
|
* @func: Function to call periodically
|
|
|
|
* @delay_us: Delay is us after which this function shall get executed
|
|
|
|
* @name: Cyclic function name/id
|
|
|
|
* @ctx: Context to pass to the function
|
|
|
|
* @return: pointer to cyclic_struct if OK, NULL on error
|
|
|
|
*/
|
|
|
|
struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us,
|
|
|
|
const char *name, void *ctx);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cyclic_unregister - Unregister a cyclic function
|
|
|
|
*
|
|
|
|
* @cyclic: Pointer to cyclic_struct of the function that shall be removed
|
|
|
|
* @return: 0 if OK, -ve on error
|
|
|
|
*/
|
|
|
|
int cyclic_unregister(struct cyclic_info *cyclic);
|
|
|
|
|
|
|
|
/**
|
cyclic: get rid of cyclic_init()
Currently, we must call cyclic_init() at some point before
cyclic_register() becomes possible. That turns out to be somewhat
awkward, especially with SPL, and has resulted in a watchdog callback
not being registered, thus causing the board to prematurely reset.
We already rely on gd->cyclic reliably being set to NULL by the asm
code that clears all of gd. Now that the cyclic list is a hlist, and
thus an empty list is represented by a NULL head pointer, and struct
cyclic_drv has no other members, we can just as well drop a level of
indirection and put the hlist_head directly in struct
global_data. This doesn't increase the size of struct global_data,
gets rid of an early malloc(), and generates slightly smaller code.
But primarily, this avoids having to call cyclic_init() early; the cyclic
infrastructure is simply ready to register callbacks as soon as we
enter C code.
We can still end up with schedule() being called from asm very early,
so we still need to check that gd itself has been properly initialized
[*], but once it has, gd->cyclic_list is perfectly fine to access, and
will just be an empty list.
As for cyclic_uninit(), it was never really the opposite of
cyclic_init() since it didn't free the struct cyclic_drv nor set
gd->cyclic to NULL. Rename it to cyclic_unregister_all() and use that
in test/, and also insert a call at the end of the board_init_f
sequence so that gd->cyclic_list is a fresh empty list before we enter
board_init_r().
A small piece of ugliness is that I had to add a cast in
cyclic_get_list() to silence a "discards 'volatile' qualifier"
warning, but that is completely equivalent to the existing handling of
the uclass_root_s list_head member.
[*] I'm not really sure where we guarantee that the register used for
gd contains 0 until it gets explicitly initialized, but that must be
the case, otherwise testing gd for being NULL would not make much sense.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Stefan Roese <sr@denx.de>
Tested-by: Stefan Roese <sr@denx.de>
Tested-by: Tim Harvey <tharvey@gateworks.com> # imx8mm-venice-*
2022-10-28 19:50:54 +08:00
|
|
|
* cyclic_unregister_all() - Clean up cyclic functions
|
2022-09-02 19:57:48 +08:00
|
|
|
*
|
|
|
|
* This removes all cyclic functions
|
|
|
|
*/
|
cyclic: get rid of cyclic_init()
Currently, we must call cyclic_init() at some point before
cyclic_register() becomes possible. That turns out to be somewhat
awkward, especially with SPL, and has resulted in a watchdog callback
not being registered, thus causing the board to prematurely reset.
We already rely on gd->cyclic reliably being set to NULL by the asm
code that clears all of gd. Now that the cyclic list is a hlist, and
thus an empty list is represented by a NULL head pointer, and struct
cyclic_drv has no other members, we can just as well drop a level of
indirection and put the hlist_head directly in struct
global_data. This doesn't increase the size of struct global_data,
gets rid of an early malloc(), and generates slightly smaller code.
But primarily, this avoids having to call cyclic_init() early; the cyclic
infrastructure is simply ready to register callbacks as soon as we
enter C code.
We can still end up with schedule() being called from asm very early,
so we still need to check that gd itself has been properly initialized
[*], but once it has, gd->cyclic_list is perfectly fine to access, and
will just be an empty list.
As for cyclic_uninit(), it was never really the opposite of
cyclic_init() since it didn't free the struct cyclic_drv nor set
gd->cyclic to NULL. Rename it to cyclic_unregister_all() and use that
in test/, and also insert a call at the end of the board_init_f
sequence so that gd->cyclic_list is a fresh empty list before we enter
board_init_r().
A small piece of ugliness is that I had to add a cast in
cyclic_get_list() to silence a "discards 'volatile' qualifier"
warning, but that is completely equivalent to the existing handling of
the uclass_root_s list_head member.
[*] I'm not really sure where we guarantee that the register used for
gd contains 0 until it gets explicitly initialized, but that must be
the case, otherwise testing gd for being NULL would not make much sense.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Stefan Roese <sr@denx.de>
Tested-by: Stefan Roese <sr@denx.de>
Tested-by: Tim Harvey <tharvey@gateworks.com> # imx8mm-venice-*
2022-10-28 19:50:54 +08:00
|
|
|
int cyclic_unregister_all(void);
|
2022-09-02 19:57:48 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* cyclic_get_list() - Get cyclic list pointer
|
|
|
|
*
|
|
|
|
* Return the cyclic list pointer
|
|
|
|
*
|
|
|
|
* @return: pointer to cyclic_list
|
|
|
|
*/
|
cyclic: switch to using hlist instead of list
A hlist is headed by just a single pointer, so can only be traversed
forwards, and insertions can only happen at the head (or before/after
an existing list member). But each list node still consists of two
pointers, so arbitrary elements can still be removed in O(1).
This is precisely what we need for the cyclic_list - we never need to
traverse it backwards, and the order the callbacks appear in the list
should really not matter.
One advantage, and the main reason for doing this switch, is that an
empty list is represented by a NULL head pointer, so unlike a
list_head, it does not need separate C code to initialize - a
memset(,0,) of the containing structure is sufficient.
This is mostly mechanical:
- The iterators are updated with an h prefix, and the type of the
temporary variable changed to struct hlist_node*.
- Adding/removing is now just hlist_add_head (and not tail) and
hlist_del().
- struct members and function return values updated.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Stefan Roese <sr@denx.de>
Tested-by: Stefan Roese <sr@denx.de>
Tested-by: Tim Harvey <tharvey@gateworks.com> # imx8mm-venice-*
2022-10-28 19:50:53 +08:00
|
|
|
struct hlist_head *cyclic_get_list(void);
|
2022-09-02 19:57:48 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* cyclic_run() - Interate over all registered cyclic functions
|
|
|
|
*
|
|
|
|
* Interate over all registered cyclic functions and if the it's function
|
|
|
|
* needs to be executed, then call into these registered functions.
|
|
|
|
*/
|
|
|
|
void cyclic_run(void);
|
2022-09-02 20:10:45 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* schedule() - Schedule all potentially waiting tasks
|
|
|
|
*
|
|
|
|
* Basically a wrapper for cyclic_run(), pontentially enhanced by some
|
|
|
|
* other parts, that need to get handled periodically.
|
|
|
|
*/
|
|
|
|
void schedule(void);
|
2022-09-02 19:57:48 +08:00
|
|
|
#else
|
|
|
|
static inline struct cyclic_info *cyclic_register(cyclic_func_t func,
|
|
|
|
uint64_t delay_us,
|
|
|
|
const char *name,
|
|
|
|
void *ctx)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int cyclic_unregister(struct cyclic_info *cyclic)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void cyclic_run(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-09-02 20:10:45 +08:00
|
|
|
static inline void schedule(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
cyclic: get rid of cyclic_init()
Currently, we must call cyclic_init() at some point before
cyclic_register() becomes possible. That turns out to be somewhat
awkward, especially with SPL, and has resulted in a watchdog callback
not being registered, thus causing the board to prematurely reset.
We already rely on gd->cyclic reliably being set to NULL by the asm
code that clears all of gd. Now that the cyclic list is a hlist, and
thus an empty list is represented by a NULL head pointer, and struct
cyclic_drv has no other members, we can just as well drop a level of
indirection and put the hlist_head directly in struct
global_data. This doesn't increase the size of struct global_data,
gets rid of an early malloc(), and generates slightly smaller code.
But primarily, this avoids having to call cyclic_init() early; the cyclic
infrastructure is simply ready to register callbacks as soon as we
enter C code.
We can still end up with schedule() being called from asm very early,
so we still need to check that gd itself has been properly initialized
[*], but once it has, gd->cyclic_list is perfectly fine to access, and
will just be an empty list.
As for cyclic_uninit(), it was never really the opposite of
cyclic_init() since it didn't free the struct cyclic_drv nor set
gd->cyclic to NULL. Rename it to cyclic_unregister_all() and use that
in test/, and also insert a call at the end of the board_init_f
sequence so that gd->cyclic_list is a fresh empty list before we enter
board_init_r().
A small piece of ugliness is that I had to add a cast in
cyclic_get_list() to silence a "discards 'volatile' qualifier"
warning, but that is completely equivalent to the existing handling of
the uclass_root_s list_head member.
[*] I'm not really sure where we guarantee that the register used for
gd contains 0 until it gets explicitly initialized, but that must be
the case, otherwise testing gd for being NULL would not make much sense.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Stefan Roese <sr@denx.de>
Tested-by: Stefan Roese <sr@denx.de>
Tested-by: Tim Harvey <tharvey@gateworks.com> # imx8mm-venice-*
2022-10-28 19:50:54 +08:00
|
|
|
static inline int cyclic_unregister_all(void)
|
2022-09-02 19:57:48 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|