mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-26 05:34:30 +08:00
ddc8d36a74
With the migration of the watchdog infrastructure to cyclic functions it's been noticed, that at least one watchdog driver is broken now. As the execution time of it's watchdog reset function is quite long. In general it's not really necessary (right now) to disable the cyclic function upon exceeding CPU time usage. So instead of disabling the cylic function in this case, let's just print a warning once to show this potential problem to the user. Signed-off-by: Stefan Roese <sr@denx.de> Suggested-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: Tom Rini <trini@konsulko.com> Cc: Pali Rohár <pali@kernel.org>
144 lines
3.3 KiB
C
144 lines
3.3 KiB
C
// 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;
|
|
|
|
void hw_watchdog_reset(void);
|
|
|
|
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) &&
|
|
(!cyclic->already_warned)) {
|
|
pr_err("cyclic function %s took too long: %lldus vs %dus max\n",
|
|
cyclic->name, cpu_time,
|
|
CONFIG_CYCLIC_MAX_CPU_TIME_US);
|
|
|
|
/*
|
|
* Don't disable this function, just warn once
|
|
* about this exceeding CPU time usage
|
|
*/
|
|
cyclic->already_warned = true;
|
|
}
|
|
}
|
|
}
|
|
gd->cyclic->cyclic_running = false;
|
|
}
|
|
|
|
void schedule(void)
|
|
{
|
|
/* The HW watchdog is not integrated into the cyclic IF (yet) */
|
|
if (IS_ENABLED(CONFIG_HW_WATCHDOG))
|
|
hw_watchdog_reset();
|
|
|
|
/*
|
|
* schedule() might get called very early before the cyclic IF is
|
|
* ready. Make sure to only call cyclic_run() when it's initalized.
|
|
*/
|
|
if (gd && gd->cyclic && gd->cyclic->cyclic_ready)
|
|
cyclic_run();
|
|
}
|
|
|
|
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;
|
|
}
|