1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * A general-purpose cyclic execution infrastructure, to allow "small" 4 * (run-time wise) functions to be executed at a specified frequency. 5 * Things like LED blinking or watchdog triggering are examples for such 6 * tasks. 7 * 8 * Copyright (C) 2022 Stefan Roese <sr@denx.de> 9 */ 10 11#include <cyclic.h> 12#include <log.h> 13#include <malloc.h> 14#include <time.h> 15#include <linux/errno.h> 16#include <linux/list.h> 17#include <asm/global_data.h> 18 19DECLARE_GLOBAL_DATA_PTR; 20 21void hw_watchdog_reset(void); 22 23struct hlist_head *cyclic_get_list(void) 24{ 25 /* Silence "discards 'volatile' qualifier" warning. */ 26 return (struct hlist_head *)&gd->cyclic_list; 27} 28 29struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us, 30 const char *name, void *ctx) 31{ 32 struct cyclic_info *cyclic; 33 34 cyclic = calloc(1, sizeof(struct cyclic_info)); 35 if (!cyclic) { 36 pr_debug("Memory allocation error\n"); 37 return NULL; 38 } 39 40 /* Store values in struct */ 41 cyclic->func = func; 42 cyclic->ctx = ctx; 43 cyclic->name = strdup(name); 44 cyclic->delay_us = delay_us; 45 cyclic->start_time_us = timer_get_us(); 46 hlist_add_head(&cyclic->list, cyclic_get_list()); 47 48 return cyclic; 49} 50 51int cyclic_unregister(struct cyclic_info *cyclic) 52{ 53 hlist_del(&cyclic->list); 54 free(cyclic); 55 56 return 0; 57} 58 59void cyclic_run(void) 60{ 61 struct cyclic_info *cyclic; 62 struct hlist_node *tmp; 63 uint64_t now, cpu_time; 64 65 /* Prevent recursion */ 66 if (gd->flags & GD_FLG_CYCLIC_RUNNING) 67 return; 68 69 gd->flags |= GD_FLG_CYCLIC_RUNNING; 70 hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) { 71 /* 72 * Check if this cyclic function needs to get called, e.g. 73 * do not call the cyclic func too often 74 */ 75 now = timer_get_us(); 76 if (time_after_eq64(now, cyclic->next_call)) { 77 /* Call cyclic function and account it's cpu-time */ 78 cyclic->next_call = now + cyclic->delay_us; 79 cyclic->func(cyclic->ctx); 80 cyclic->run_cnt++; 81 cpu_time = timer_get_us() - now; 82 cyclic->cpu_time_us += cpu_time; 83 84 /* Check if cpu-time exceeds max allowed time */ 85 if ((cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) && 86 (!cyclic->already_warned)) { 87 pr_err("cyclic function %s took too long: %lldus vs %dus max\n", 88 cyclic->name, cpu_time, 89 CONFIG_CYCLIC_MAX_CPU_TIME_US); 90 91 /* 92 * Don't disable this function, just warn once 93 * about this exceeding CPU time usage 94 */ 95 cyclic->already_warned = true; 96 } 97 } 98 } 99 gd->flags &= ~GD_FLG_CYCLIC_RUNNING; 100} 101 102void schedule(void) 103{ 104 /* The HW watchdog is not integrated into the cyclic IF (yet) */ 105 if (IS_ENABLED(CONFIG_HW_WATCHDOG)) 106 hw_watchdog_reset(); 107 108 /* 109 * schedule() might get called very early before the cyclic IF is 110 * ready. Make sure to only call cyclic_run() when it's initalized. 111 */ 112 if (gd) 113 cyclic_run(); 114} 115 116int cyclic_unregister_all(void) 117{ 118 struct cyclic_info *cyclic; 119 struct hlist_node *tmp; 120 121 hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) 122 cyclic_unregister(cyclic); 123 124 return 0; 125} 126