Merge branch '2022-09-13-add-support-for-cyclic-function-execution' into next
[platform/kernel/u-boot.git] / common / cyclic.c
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
19 DECLARE_GLOBAL_DATA_PTR;
20
21 struct list_head *cyclic_get_list(void)
22 {
23         return &gd->cyclic->cyclic_list;
24 }
25
26 struct cyclic_info *cyclic_register(cyclic_func_t func, uint64_t delay_us,
27                                     const char *name, void *ctx)
28 {
29         struct cyclic_info *cyclic;
30
31         if (!gd->cyclic->cyclic_ready) {
32                 pr_debug("Cyclic IF not ready yet\n");
33                 return NULL;
34         }
35
36         cyclic = calloc(1, sizeof(struct cyclic_info));
37         if (!cyclic) {
38                 pr_debug("Memory allocation error\n");
39                 return NULL;
40         }
41
42         /* Store values in struct */
43         cyclic->func = func;
44         cyclic->ctx = ctx;
45         cyclic->name = strdup(name);
46         cyclic->delay_us = delay_us;
47         cyclic->start_time_us = timer_get_us();
48         list_add_tail(&cyclic->list, &gd->cyclic->cyclic_list);
49
50         return cyclic;
51 }
52
53 int cyclic_unregister(struct cyclic_info *cyclic)
54 {
55         list_del(&cyclic->list);
56         free(cyclic);
57
58         return 0;
59 }
60
61 void cyclic_run(void)
62 {
63         struct cyclic_info *cyclic, *tmp;
64         uint64_t now, cpu_time;
65
66         /* Prevent recursion */
67         if (gd->cyclic->cyclic_running)
68                 return;
69
70         gd->cyclic->cyclic_running = true;
71         list_for_each_entry_safe(cyclic, tmp, &gd->cyclic->cyclic_list, list) {
72                 /*
73                  * Check if this cyclic function needs to get called, e.g.
74                  * do not call the cyclic func too often
75                  */
76                 now = timer_get_us();
77                 if (time_after_eq64(now, cyclic->next_call)) {
78                         /* Call cyclic function and account it's cpu-time */
79                         cyclic->next_call = now + cyclic->delay_us;
80                         cyclic->func(cyclic->ctx);
81                         cyclic->run_cnt++;
82                         cpu_time = timer_get_us() - now;
83                         cyclic->cpu_time_us += cpu_time;
84
85                         /* Check if cpu-time exceeds max allowed time */
86                         if (cpu_time > CONFIG_CYCLIC_MAX_CPU_TIME_US) {
87                                 pr_err("cyclic function %s took too long: %lldus vs %dus max, disabling\n",
88                                        cyclic->name, cpu_time,
89                                        CONFIG_CYCLIC_MAX_CPU_TIME_US);
90
91                                 /* Unregister this cyclic function */
92                                 cyclic_unregister(cyclic);
93                         }
94                 }
95         }
96         gd->cyclic->cyclic_running = false;
97 }
98
99 int cyclic_uninit(void)
100 {
101         struct cyclic_info *cyclic, *tmp;
102
103         list_for_each_entry_safe(cyclic, tmp, &gd->cyclic->cyclic_list, list)
104                 cyclic_unregister(cyclic);
105         gd->cyclic->cyclic_ready = false;
106
107         return 0;
108 }
109
110 int cyclic_init(void)
111 {
112         int size = sizeof(struct cyclic_drv);
113
114         gd->cyclic = (struct cyclic_drv *)malloc(size);
115         if (!gd->cyclic)
116                 return -ENOMEM;
117
118         memset(gd->cyclic, '\0', size);
119         INIT_LIST_HEAD(&gd->cyclic->cyclic_list);
120         gd->cyclic->cyclic_ready = true;
121
122         return 0;
123 }