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