]>
Commit | Line | Data |
---|---|---|
c2c69718 SR |
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 <[email protected]> | |
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 | ||
881d4108 SR |
21 | void hw_watchdog_reset(void); |
22 | ||
28968394 | 23 | struct hlist_head *cyclic_get_list(void) |
c2c69718 | 24 | { |
50128aeb RV |
25 | /* Silence "discards 'volatile' qualifier" warning. */ |
26 | return (struct hlist_head *)&gd->cyclic_list; | |
c2c69718 SR |
27 | } |
28 | ||
29 | struct 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 | ||
c2c69718 SR |
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(); | |
50128aeb | 46 | hlist_add_head(&cyclic->list, cyclic_get_list()); |
c2c69718 SR |
47 | |
48 | return cyclic; | |
49 | } | |
50 | ||
51 | int cyclic_unregister(struct cyclic_info *cyclic) | |
52 | { | |
28968394 | 53 | hlist_del(&cyclic->list); |
c2c69718 SR |
54 | free(cyclic); |
55 | ||
56 | return 0; | |
57 | } | |
58 | ||
59 | void cyclic_run(void) | |
60 | { | |
28968394 RV |
61 | struct cyclic_info *cyclic; |
62 | struct hlist_node *tmp; | |
c2c69718 SR |
63 | uint64_t now, cpu_time; |
64 | ||
65 | /* Prevent recursion */ | |
d7de5ef6 | 66 | if (gd->flags & GD_FLG_CYCLIC_RUNNING) |
c2c69718 SR |
67 | return; |
68 | ||
d7de5ef6 | 69 | gd->flags |= GD_FLG_CYCLIC_RUNNING; |
50128aeb | 70 | hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) { |
c2c69718 SR |
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 */ | |
ddc8d36a SR |
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", | |
c2c69718 SR |
88 | cyclic->name, cpu_time, |
89 | CONFIG_CYCLIC_MAX_CPU_TIME_US); | |
90 | ||
ddc8d36a SR |
91 | /* |
92 | * Don't disable this function, just warn once | |
93 | * about this exceeding CPU time usage | |
94 | */ | |
95 | cyclic->already_warned = true; | |
c2c69718 SR |
96 | } |
97 | } | |
98 | } | |
d7de5ef6 | 99 | gd->flags &= ~GD_FLG_CYCLIC_RUNNING; |
c2c69718 SR |
100 | } |
101 | ||
881d4108 SR |
102 | void 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 | */ | |
50128aeb | 112 | if (gd) |
881d4108 SR |
113 | cyclic_run(); |
114 | } | |
115 | ||
50128aeb | 116 | int cyclic_unregister_all(void) |
c2c69718 | 117 | { |
28968394 RV |
118 | struct cyclic_info *cyclic; |
119 | struct hlist_node *tmp; | |
c2c69718 | 120 | |
50128aeb | 121 | hlist_for_each_entry_safe(cyclic, tmp, cyclic_get_list(), list) |
c2c69718 | 122 | cyclic_unregister(cyclic); |
c2c69718 SR |
123 | |
124 | return 0; | |
125 | } |