1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 * Copyright (c) 2017 Intel Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #ifndef ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_
9 #define ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_
10
11 #include <zephyr/drivers/adc.h>
12 #include <zephyr/sys/atomic.h>
13
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17
18 struct adc_context;
19
20 /*
21 * Each driver should provide implementations of the following two functions:
22 * - adc_context_start_sampling() that will be called when a sampling (of one
23 * or more channels, depending on the realized sequence) is to be started
24 * - adc_context_update_buffer_pointer() that will be called when the sample
25 * buffer pointer should be prepared for writing of next sampling results,
26 * the "repeat_sampling" parameter indicates if the results should be written
27 * in the same place as before (when true) or as consecutive ones (otherwise).
28 */
29 static void adc_context_start_sampling(struct adc_context *ctx);
30 static void adc_context_update_buffer_pointer(struct adc_context *ctx,
31 bool repeat_sampling);
32 /*
33 * If a given driver uses some dedicated hardware timer to trigger consecutive
34 * samplings, it should implement also the following two functions. Otherwise,
35 * it should define the ADC_CONTEXT_USES_KERNEL_TIMER macro to enable parts of
36 * this module that utilize a standard kernel timer.
37 */
38 static void adc_context_enable_timer(struct adc_context *ctx);
39 static void adc_context_disable_timer(struct adc_context *ctx);
40
41 /*
42 * If a driver needs to do something after a context complete then
43 * then this optional function can be overwritten. This will be called
44 * after a sequence has ended, and *not* when restarted with ADC_ACTION_REPEAT.
45 * To enable this function define ADC_CONTEXT_ENABLE_ON_COMPLETE.
46 */
47 #ifdef ADC_CONTEXT_ENABLE_ON_COMPLETE
48 static void adc_context_on_complete(struct adc_context *ctx, int status);
49 #endif /* ADC_CONTEXT_ENABLE_ON_COMPLETE */
50
51 struct adc_context {
52 atomic_t sampling_requested;
53 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
54 struct k_timer timer;
55 #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
56
57 struct k_sem lock;
58 struct k_sem sync;
59 int status;
60
61 #ifdef CONFIG_ADC_ASYNC
62 struct k_poll_signal *signal;
63 bool asynchronous;
64 #endif /* CONFIG_ADC_ASYNC */
65
66 struct adc_sequence sequence;
67 struct adc_sequence_options options;
68 uint16_t sampling_index;
69 };
70
71 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
72 #define ADC_CONTEXT_INIT_TIMER(_data, _ctx_name) \
73 ._ctx_name.timer = Z_TIMER_INITIALIZER(_data._ctx_name.timer, \
74 adc_context_on_timer_expired, \
75 NULL)
76 #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
77
78 #define ADC_CONTEXT_INIT_LOCK(_data, _ctx_name) \
79 ._ctx_name.lock = Z_SEM_INITIALIZER(_data._ctx_name.lock, 0, 1)
80
81 #define ADC_CONTEXT_INIT_SYNC(_data, _ctx_name) \
82 ._ctx_name.sync = Z_SEM_INITIALIZER(_data._ctx_name.sync, 0, 1)
83
84 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
85 static void adc_context_on_timer_expired(struct k_timer *timer_id);
86 #endif
87
adc_context_init(struct adc_context * ctx)88 static inline void adc_context_init(struct adc_context *ctx)
89 {
90 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
91 k_timer_init(&ctx->timer, adc_context_on_timer_expired, NULL);
92 #endif
93 k_sem_init(&ctx->lock, 0, 1);
94 k_sem_init(&ctx->sync, 0, 1);
95 }
96
adc_context_request_next_sampling(struct adc_context * ctx)97 static inline void adc_context_request_next_sampling(struct adc_context *ctx)
98 {
99 if (atomic_inc(&ctx->sampling_requested) == 0) {
100 adc_context_start_sampling(ctx);
101 } else {
102 /*
103 * If a sampling was already requested and was not finished yet,
104 * do not start another one from here, this will be done from
105 * adc_context_on_sampling_done() after the current sampling is
106 * complete. Instead, note this fact, and inform the user about
107 * it after the sequence is done.
108 */
109 ctx->status = -EBUSY;
110 }
111 }
112
113 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
adc_context_enable_timer(struct adc_context * ctx)114 static inline void adc_context_enable_timer(struct adc_context *ctx)
115 {
116 k_timer_start(&ctx->timer, K_NO_WAIT, K_USEC(ctx->options.interval_us));
117 }
118
adc_context_disable_timer(struct adc_context * ctx)119 static inline void adc_context_disable_timer(struct adc_context *ctx)
120 {
121 k_timer_stop(&ctx->timer);
122 }
123
adc_context_on_timer_expired(struct k_timer * timer_id)124 static void adc_context_on_timer_expired(struct k_timer *timer_id)
125 {
126 struct adc_context *ctx =
127 CONTAINER_OF(timer_id, struct adc_context, timer);
128
129 adc_context_request_next_sampling(ctx);
130 }
131 #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
132
adc_context_lock(struct adc_context * ctx,bool asynchronous,struct k_poll_signal * signal)133 static inline void adc_context_lock(struct adc_context *ctx,
134 bool asynchronous,
135 struct k_poll_signal *signal)
136 {
137 k_sem_take(&ctx->lock, K_FOREVER);
138
139 #ifdef CONFIG_ADC_ASYNC
140 ctx->asynchronous = asynchronous;
141 ctx->signal = signal;
142 #endif /* CONFIG_ADC_ASYNC */
143 }
144
adc_context_release(struct adc_context * ctx,int status)145 static inline void adc_context_release(struct adc_context *ctx, int status)
146 {
147 #ifdef CONFIG_ADC_ASYNC
148 if (ctx->asynchronous && (status == 0)) {
149 return;
150 }
151 #endif /* CONFIG_ADC_ASYNC */
152
153 k_sem_give(&ctx->lock);
154 }
155
adc_context_unlock_unconditionally(struct adc_context * ctx)156 static inline void adc_context_unlock_unconditionally(struct adc_context *ctx)
157 {
158 if (!k_sem_count_get(&ctx->lock)) {
159 k_sem_give(&ctx->lock);
160 }
161 }
162
adc_context_wait_for_completion(struct adc_context * ctx)163 static inline int adc_context_wait_for_completion(struct adc_context *ctx)
164 {
165 #ifdef CONFIG_ADC_ASYNC
166 if (ctx->asynchronous) {
167 return 0;
168 }
169 #endif /* CONFIG_ADC_ASYNC */
170
171 k_sem_take(&ctx->sync, K_FOREVER);
172 return ctx->status;
173 }
174
adc_context_complete(struct adc_context * ctx,int status)175 static inline void adc_context_complete(struct adc_context *ctx, int status)
176 {
177 #ifdef ADC_CONTEXT_ENABLE_ON_COMPLETE
178 adc_context_on_complete(ctx, status);
179 #endif /* ADC_CONTEXT_ENABLE_ON_COMPLETE */
180
181 #ifdef CONFIG_ADC_ASYNC
182 if (ctx->asynchronous) {
183 if (ctx->signal) {
184 k_poll_signal_raise(ctx->signal, status);
185 }
186
187 k_sem_give(&ctx->lock);
188 return;
189 }
190 #endif /* CONFIG_ADC_ASYNC */
191
192 /*
193 * Override the status only when an error is signaled to this function.
194 * Please note that adc_context_request_next_sampling() might have set
195 * this field.
196 */
197 if (status != 0) {
198 ctx->status = status;
199 }
200 k_sem_give(&ctx->sync);
201 }
202
adc_context_start_read(struct adc_context * ctx,const struct adc_sequence * sequence)203 static inline void adc_context_start_read(struct adc_context *ctx,
204 const struct adc_sequence *sequence)
205 {
206 ctx->sequence = *sequence;
207 ctx->status = 0;
208
209 if (sequence->options) {
210 ctx->options = *sequence->options;
211 ctx->sequence.options = &ctx->options;
212 ctx->sampling_index = 0U;
213
214 if (ctx->options.interval_us != 0U) {
215 atomic_set(&ctx->sampling_requested, 0);
216 adc_context_enable_timer(ctx);
217 return;
218 }
219 }
220
221 adc_context_start_sampling(ctx);
222 }
223
224 /*
225 * This function should be called after a sampling (of one or more channels,
226 * depending on the realized sequence) is done. It calls the defined callback
227 * function if required and takes further actions accordingly.
228 */
adc_context_on_sampling_done(struct adc_context * ctx,const struct device * dev)229 static inline void adc_context_on_sampling_done(struct adc_context *ctx,
230 const struct device *dev)
231 {
232 if (ctx->sequence.options) {
233 adc_sequence_callback callback = ctx->options.callback;
234 enum adc_action action;
235 bool finish = false;
236 bool repeat = false;
237
238 if (callback) {
239 action = callback(dev,
240 &ctx->sequence,
241 ctx->sampling_index);
242 } else {
243 action = ADC_ACTION_CONTINUE;
244 }
245
246 switch (action) {
247 case ADC_ACTION_REPEAT:
248 repeat = true;
249 break;
250 case ADC_ACTION_FINISH:
251 finish = true;
252 break;
253 default: /* ADC_ACTION_CONTINUE */
254 if (ctx->sampling_index <
255 ctx->options.extra_samplings) {
256 ++ctx->sampling_index;
257 } else {
258 finish = true;
259 }
260 }
261
262 if (!finish) {
263 adc_context_update_buffer_pointer(ctx, repeat);
264
265 /*
266 * Immediately start the next sampling if working with
267 * a zero interval or if the timer expired again while
268 * the current sampling was in progress.
269 */
270 if (ctx->options.interval_us == 0U) {
271 adc_context_start_sampling(ctx);
272 } else if (atomic_dec(&ctx->sampling_requested) > 1) {
273 adc_context_start_sampling(ctx);
274 }
275
276 return;
277 }
278
279 if (ctx->options.interval_us != 0U) {
280 adc_context_disable_timer(ctx);
281 }
282 }
283
284 adc_context_complete(ctx, 0);
285 }
286
287 #ifdef __cplusplus
288 }
289 #endif
290
291 #endif /* ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_ */
292