1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Copyright (c) 2024, Linaro Limited
4  */
5 
6 #ifndef __KERNEL_CALLOUT_H
7 #define __KERNEL_CALLOUT_H
8 
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <sys/queue.h>
12 
13 /*
14  * struct callout - callout reference
15  * @callback:	  function to be called when a callout expires
16  * @expiry_value: callout expiry time counter value
17  * @period:	  ticks to next timeout
18  * @link:	  linked list element
19  *
20  * @callback is called from an interrupt handler so thread resources must
21  * not be used. The main callout service lock is held while @callback is
22  * called so callout_rem() and callout_add() can't be used, but it is safe
23  * to call callout_set_next_timeout() if the call period should be changed.
24  * @callback returns true if it should be called again in @period ticks
25  * or false if the callout should be removed and inactivated. Returning
26  * false from @callback is the equivalent of calling callout_rem() on the
27  * callout reference.
28  */
29 struct callout {
30 	bool (*callback)(struct callout *co);
31 	uint64_t expiry_value;
32 	uint64_t period;
33 	TAILQ_ENTRY(callout) link;
34 };
35 
36 /*
37  * callout_add() - Add a callout
38  * @co:		callout reference
39  * @callback:	callback function accociated with the callout
40  * @ms:		time to next callout in milliseconds
41  *
42  * Adds a callout to the callout service with an associated callback
43  * function @callback that is to be called in @ms milliseconds.
44  *
45  * If callout_add() is called before callout_service_init() has been called
46  * then it will be called @ms milliseconds after callout_service_init() has
47  * been called.
48  *
49  * The callout structure can reside in global data or on the heap. It's
50  * safe to embed it inside another struct, but it must not be freed until
51  * removed with callout_rem() or equivalent.
52  *
53  * The function takes the main callout service for synchronization so it
54  * can't be called from within a callback function in a callout or there's
55  * deadlock.
56  */
57 void callout_add(struct callout *co, bool (*callback)(struct callout *co),
58 		 uint32_t ms);
59 
60 /*
61  * callout_rem() - Remove a callout
62  * @co:		callout reference
63  *
64  * Removes a callout previously added to the callout service with
65  * callout_add(). Note that when the callback function in a callout
66  * returns false the callout is also removed.
67  *
68  * It's safe to try to remove a callback even if it isn't active any
69  * longer. Nothing will happen in that case, but it's guaranteed to be
70  * inactive and it's safe to free the memory after callout_rem() has
71  * returned.
72  */
73 void callout_rem(struct callout *co);
74 
75 /*
76  * callout_set_next_timeout() - set time to next callout
77  * @co:		callout reference
78  * @ms:		time to next callout in milliseconds
79  *
80  * Updates the @co->ticks field with the new number of ticks based on @ms.
81  * This value is used to when to calculate the time of the next callout
82  * following then one already set.
83  *
84  * Must only be called from @co->callback() when the callout is triggered.
85  */
86 void callout_set_next_timeout(struct callout *co, uint32_t ms);
87 
88 /*
89  * struct callout_timer_desc - callout timer descriptor
90  * @disable_timeout:	disables the timer from triggering an interrupt
91  * @set_next_timeout:	sets the next timeout and enables the timer
92  * @ms_to_ticks:	converts milliseconds to ticks, the counter value
93  *			unit
94  * @get_now:		get the current counter value
95  * @is_per_cpu:		flag to indicate if this timer is per CPU (true) or
96  *			global (false).
97  *
98  * This descriptor provides an abstract timer interface first used by
99  * callout_service_init() and then stored to be used by
100  * callout_service_cb().
101  *
102  * When @is_per_cpu is true there is one private timer per CPU so
103  * @disable_timeout() and @set_next_timeout() only affects the timer on the
104  * current CPU. If for instance @set_next_timeout() is called on a new CPU
105  * compared to last time the timer on the old CPU will remain unchanged.
106  * Timer interrupts may trigger based on obsolete configuration, the
107  * callout service is expected to handle this gracefully.
108  */
109 struct callout_timer_desc {
110 	void (*disable_timeout)(const struct callout_timer_desc *desc);
111 	void (*set_next_timeout)(const struct callout_timer_desc *desc,
112 				 uint64_t expiry_value);
113 	uint64_t (*ms_to_ticks)(const struct callout_timer_desc *desc,
114 				uint32_t ms);
115 	uint64_t (*get_now)(const struct callout_timer_desc *desc);
116 	bool is_per_cpu;
117 };
118 
119 /*
120  * callout_service_init() - Initialize the callout service
121  * @desc:	Pointer to the timer interface
122  *
123  * The callout service is initialized with the supplied timer interface
124  */
125 void callout_service_init(const struct callout_timer_desc *desc);
126 
127 /*
128  * callout_service_cb() - Callout service callback
129  *
130  * Called from interrupt service function for the timer.
131  */
132 void callout_service_cb(void);
133 
134 #endif /*__KERNEL_CALLOUT_H*/
135