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 #ifndef ADC_CONTEXT_WAIT_FOR_COMPLETION_TIMEOUT
52 #define ADC_CONTEXT_WAIT_FOR_COMPLETION_TIMEOUT K_FOREVER
53 #endif
54 
55 struct adc_context {
56 	atomic_t sampling_requested;
57 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
58 	struct k_timer timer;
59 #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
60 
61 	struct k_sem lock;
62 	struct k_sem sync;
63 	int status;
64 
65 #ifdef CONFIG_ADC_ASYNC
66 	struct k_poll_signal *signal;
67 	bool asynchronous;
68 #endif /* CONFIG_ADC_ASYNC */
69 
70 	struct adc_sequence sequence;
71 	struct adc_sequence_options options;
72 	uint16_t sampling_index;
73 };
74 
75 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
76 #define ADC_CONTEXT_INIT_TIMER(_data, _ctx_name) \
77 	._ctx_name.timer = Z_TIMER_INITIALIZER(_data._ctx_name.timer, \
78 						adc_context_on_timer_expired, \
79 						NULL)
80 #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
81 
82 #define ADC_CONTEXT_INIT_LOCK(_data, _ctx_name) \
83 	._ctx_name.lock = Z_SEM_INITIALIZER(_data._ctx_name.lock, 0, 1)
84 
85 #define ADC_CONTEXT_INIT_SYNC(_data, _ctx_name) \
86 	._ctx_name.sync = Z_SEM_INITIALIZER(_data._ctx_name.sync, 0, 1)
87 
88 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
89 static void adc_context_on_timer_expired(struct k_timer *timer_id);
90 #endif
91 
adc_context_init(struct adc_context * ctx)92 static inline void adc_context_init(struct adc_context *ctx)
93 {
94 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
95 	k_timer_init(&ctx->timer, adc_context_on_timer_expired, NULL);
96 #endif
97 	k_sem_init(&ctx->lock, 0, 1);
98 	k_sem_init(&ctx->sync, 0, 1);
99 }
100 
adc_context_request_next_sampling(struct adc_context * ctx)101 static inline void adc_context_request_next_sampling(struct adc_context *ctx)
102 {
103 	if (atomic_inc(&ctx->sampling_requested) == 0) {
104 		adc_context_start_sampling(ctx);
105 	} else {
106 		/*
107 		 * If a sampling was already requested and was not finished yet,
108 		 * do not start another one from here, this will be done from
109 		 * adc_context_on_sampling_done() after the current sampling is
110 		 * complete. Instead, note this fact, and inform the user about
111 		 * it after the sequence is done.
112 		 */
113 		ctx->status = -EBUSY;
114 	}
115 }
116 
117 #ifdef ADC_CONTEXT_USES_KERNEL_TIMER
adc_context_enable_timer(struct adc_context * ctx)118 static inline void adc_context_enable_timer(struct adc_context *ctx)
119 {
120 	k_timer_start(&ctx->timer, K_NO_WAIT, K_USEC(ctx->options.interval_us));
121 }
122 
adc_context_disable_timer(struct adc_context * ctx)123 static inline void adc_context_disable_timer(struct adc_context *ctx)
124 {
125 	k_timer_stop(&ctx->timer);
126 }
127 
adc_context_on_timer_expired(struct k_timer * timer_id)128 static void adc_context_on_timer_expired(struct k_timer *timer_id)
129 {
130 	struct adc_context *ctx =
131 		CONTAINER_OF(timer_id, struct adc_context, timer);
132 
133 	adc_context_request_next_sampling(ctx);
134 }
135 #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */
136 
adc_context_lock(struct adc_context * ctx,bool asynchronous,struct k_poll_signal * signal)137 static inline void adc_context_lock(struct adc_context *ctx,
138 				    bool asynchronous,
139 				    struct k_poll_signal *signal)
140 {
141 	k_sem_take(&ctx->lock, K_FOREVER);
142 
143 #ifdef CONFIG_ADC_ASYNC
144 	ctx->asynchronous = asynchronous;
145 	ctx->signal = signal;
146 #endif /* CONFIG_ADC_ASYNC */
147 }
148 
adc_context_release(struct adc_context * ctx,int status)149 static inline void adc_context_release(struct adc_context *ctx, int status)
150 {
151 #ifdef CONFIG_ADC_ASYNC
152 	if (ctx->asynchronous && (status == 0)) {
153 		return;
154 	}
155 #endif /* CONFIG_ADC_ASYNC */
156 
157 	k_sem_give(&ctx->lock);
158 }
159 
adc_context_unlock_unconditionally(struct adc_context * ctx)160 static inline void adc_context_unlock_unconditionally(struct adc_context *ctx)
161 {
162 	if (!k_sem_count_get(&ctx->lock)) {
163 		k_sem_give(&ctx->lock);
164 	}
165 }
166 
adc_context_wait_for_completion(struct adc_context * ctx)167 static inline int adc_context_wait_for_completion(struct adc_context *ctx)
168 {
169 #ifdef CONFIG_ADC_ASYNC
170 	if (ctx->asynchronous) {
171 		return 0;
172 	}
173 #endif /* CONFIG_ADC_ASYNC */
174 
175 	int status = k_sem_take(&ctx->sync, ADC_CONTEXT_WAIT_FOR_COMPLETION_TIMEOUT);
176 
177 	if (status != 0) {
178 		ctx->status = status;
179 	}
180 
181 	return ctx->status;
182 }
183 
adc_context_complete(struct adc_context * ctx,int status)184 static inline void adc_context_complete(struct adc_context *ctx, int status)
185 {
186 #ifdef ADC_CONTEXT_ENABLE_ON_COMPLETE
187 	adc_context_on_complete(ctx, status);
188 #endif /* ADC_CONTEXT_ENABLE_ON_COMPLETE */
189 
190 #ifdef CONFIG_ADC_ASYNC
191 	if (ctx->asynchronous) {
192 		if (ctx->signal) {
193 			k_poll_signal_raise(ctx->signal, status);
194 		}
195 
196 		k_sem_give(&ctx->lock);
197 		return;
198 	}
199 #endif /* CONFIG_ADC_ASYNC */
200 
201 	/*
202 	 * Override the status only when an error is signaled to this function.
203 	 * Please note that adc_context_request_next_sampling() might have set
204 	 * this field.
205 	 */
206 	if (status != 0) {
207 		ctx->status = status;
208 	}
209 	k_sem_give(&ctx->sync);
210 }
211 
adc_context_start_read(struct adc_context * ctx,const struct adc_sequence * sequence)212 static inline void adc_context_start_read(struct adc_context *ctx,
213 					  const struct adc_sequence *sequence)
214 {
215 	ctx->sequence = *sequence;
216 	ctx->status = 0;
217 
218 	if (sequence->options) {
219 		ctx->options = *sequence->options;
220 		ctx->sequence.options = &ctx->options;
221 		ctx->sampling_index = 0U;
222 
223 		if (ctx->options.interval_us != 0U) {
224 			atomic_set(&ctx->sampling_requested, 0);
225 			adc_context_enable_timer(ctx);
226 			return;
227 		}
228 	}
229 
230 	adc_context_start_sampling(ctx);
231 }
232 
233 /*
234  * This function should be called after a sampling (of one or more channels,
235  * depending on the realized sequence) is done. It calls the defined callback
236  * function if required and takes further actions accordingly.
237  */
adc_context_on_sampling_done(struct adc_context * ctx,const struct device * dev)238 static inline void adc_context_on_sampling_done(struct adc_context *ctx,
239 						const struct device *dev)
240 {
241 	if (ctx->sequence.options) {
242 		adc_sequence_callback callback = ctx->options.callback;
243 		enum adc_action action;
244 		bool finish = false;
245 		bool repeat = false;
246 
247 		if (callback) {
248 			action = callback(dev,
249 					  &ctx->sequence,
250 					  ctx->sampling_index);
251 		} else {
252 			action = ADC_ACTION_CONTINUE;
253 		}
254 
255 		switch (action) {
256 		case ADC_ACTION_REPEAT:
257 			repeat = true;
258 			break;
259 		case ADC_ACTION_FINISH:
260 			finish = true;
261 			break;
262 		default: /* ADC_ACTION_CONTINUE */
263 			if (ctx->sampling_index <
264 			    ctx->options.extra_samplings) {
265 				++ctx->sampling_index;
266 			} else {
267 				finish = true;
268 			}
269 		}
270 
271 		if (!finish) {
272 			adc_context_update_buffer_pointer(ctx, repeat);
273 
274 			/*
275 			 * Immediately start the next sampling if working with
276 			 * a zero interval or if the timer expired again while
277 			 * the current sampling was in progress.
278 			 */
279 			if (ctx->options.interval_us == 0U) {
280 				adc_context_start_sampling(ctx);
281 			} else if (atomic_dec(&ctx->sampling_requested) > 1) {
282 				adc_context_start_sampling(ctx);
283 			}
284 
285 			return;
286 		}
287 
288 		if (ctx->options.interval_us != 0U) {
289 			adc_context_disable_timer(ctx);
290 		}
291 	}
292 
293 	adc_context_complete(ctx, 0);
294 }
295 
296 #ifdef __cplusplus
297 }
298 #endif
299 
300 #endif /* ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_ */
301