diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Kconfig | 20 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/board_f.c | 2 | ||||
-rw-r--r-- | common/board_r.c | 2 | ||||
-rw-r--r-- | common/cyclic.c | 123 |
5 files changed, 148 insertions, 0 deletions
diff --git a/common/Kconfig b/common/Kconfig index 2c3f7f4274..3e44acd2be 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -545,6 +545,26 @@ config DISPLAY_BOARDINFO_LATE menu "Start-up hooks" +config CYCLIC + bool "General-purpose cyclic execution mechanism" + help + This enables 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. + +if CYCLIC + +config CYCLIC_MAX_CPU_TIME_US + int "Sets the max allowed time for a cyclic function in us" + default 1000 + help + The max allowed time for a cyclic function in us. If a functions + takes longer than this duration this function will get unregistered + automatically. + +endif # CYCLIC + config EVENT bool "General-purpose event-handling mechanism" default y if SANDBOX diff --git a/common/Makefile b/common/Makefile index 2ed8672c3a..1d56c9f289 100644 --- a/common/Makefile +++ b/common/Makefile @@ -84,6 +84,7 @@ obj-y += malloc_simple.o endif endif +obj-$(CONFIG_CYCLIC) += cyclic.o obj-$(CONFIG_$(SPL_TPL_)EVENT) += event.o obj-$(CONFIG_$(SPL_TPL_)HASH) += hash.o diff --git a/common/board_f.c b/common/board_f.c index 18e2246733..deb46be182 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -16,6 +16,7 @@ #include <console.h> #include <cpu.h> #include <cpu_func.h> +#include <cyclic.h> #include <display_options.h> #include <dm.h> #include <env.h> @@ -828,6 +829,7 @@ static const init_fnc_t init_sequence_f[] = { initf_malloc, log_init, initf_bootstage, /* uses its own timer, so does not need DM */ + cyclic_init, event_init, #ifdef CONFIG_BLOBLIST bloblist_init, diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..062bc3e688 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -13,6 +13,7 @@ #include <api.h> #include <bootstage.h> #include <cpu_func.h> +#include <cyclic.h> #include <display_options.h> #include <exports.h> #ifdef CONFIG_MTD_NOR_FLASH @@ -611,6 +612,7 @@ static init_fnc_t init_sequence_r[] = { #endif initr_barrier, initr_malloc, + cyclic_init, log_init, initr_bootstage, /* Needs malloc() but has its own timer */ #if defined(CONFIG_CONSOLE_RECORD) diff --git a/common/cyclic.c b/common/cyclic.c new file mode 100644 index 0000000000..cd5dcb1f2b --- /dev/null +++ b/common/cyclic.c @@ -0,0 +1,123 @@ +// 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> + */ + +#include <cyclic.h> +#include <log.h> +#include <malloc.h> +#include <time.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct list_head *cyclic_get_list(void) +{ + return &gd->cyclic->cyclic_list; +} + +struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us, + const char *name, void *ctx) +{ + struct cyclic_info *cyclic; + + if (!gd->cyclic->cyclic_ready) { + pr_debug("Cyclic IF not ready yet\n"); + return NULL; + } + + cyclic = calloc(1, sizeof(struct cyclic_info)); + if (!cyclic) { + pr_debug("Memory allocation error\n"); + return NULL; + } + + /* Store values in struct */ + cyclic->func = func; + cyclic->ctx = ctx; + cyclic->name = strdup(name); + cyclic->delay_us = delay_us; + cyclic->start_time_us = timer_get_us(); + list_add_tail(&cyclic->list, &gd->cyclic->cyclic_list); + + return cyclic; +} + +int cyclic_unregister(struct cyclic_info *cyclic) +{ + list_del(&cyclic->list); + free(cyclic); + + return 0; +} + +void cyclic_run(void) +{ + struct cyclic_info *cyclic, *tmp; + uint64_t now, cpu_time; + + /* Prevent recursion */ + if (gd->cyclic->cyclic_running) + return; + + gd->cyclic->cyclic_running = true; + list_for_each_entry_safe(cyclic, tmp, &gd->cyclic->cyclic_list, list) { + /* + * Check if this cyclic function needs to get called, e.g. + * do not call the cyclic func too often + */ + now = timer_get_us(); + if (time_after_eq64(now, cyclic->next_call)) { + /* Call cyclic function and account it's cpu-time */ + cyclic->next_call = now + cyclic->delay_us; + cyclic->func(cyclic->ctx); + cyclic->run_cnt++; + cpu_time = timer_get_us() - now; + cyclic->cpu_time_us += cpu_time; + + /* Check if cpu-time exceeds max allowed time */ + if (cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) { + pr_err("cyclic function %s took too long: %lldus vs %dus max, disabling\n", + cyclic->name, cpu_time, + CONFIG_CYCLIC_MAX_CPU_TIME_US); + + /* Unregister this cyclic function */ + cyclic_unregister(cyclic); + } + } + } + gd->cyclic->cyclic_running = false; +} + +int cyclic_uninit(void) +{ + struct cyclic_info *cyclic, *tmp; + + list_for_each_entry_safe(cyclic, tmp, &gd->cyclic->cyclic_list, list) + cyclic_unregister(cyclic); + gd->cyclic->cyclic_ready = false; + + return 0; +} + +int cyclic_init(void) +{ + int size = sizeof(struct cyclic_drv); + + gd->cyclic = (struct cyclic_drv *)malloc(size); + if (!gd->cyclic) + return -ENOMEM; + + memset(gd->cyclic, '\0', size); + INIT_LIST_HEAD(&gd->cyclic->cyclic_list); + gd->cyclic->cyclic_ready = true; + + return 0; +} |