|  | /* | 
|  | * Copyright (c) 2018 Nordic Semiconductor ASA | 
|  | * Copyright (c) 2017 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #ifndef ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_ | 
|  | #define ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_ | 
|  |  | 
|  | #include <drivers/adc.h> | 
|  | #include <sys/atomic.h> | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | extern "C" { | 
|  | #endif | 
|  |  | 
|  | struct adc_context; | 
|  |  | 
|  | /* | 
|  | * Each driver should provide implementations of the following two functions: | 
|  | * - adc_context_start_sampling() that will be called when a sampling (of one | 
|  | *   or more channels, depending on the realized sequence) is to be started | 
|  | * - adc_context_update_buffer_pointer() that will be called when the sample | 
|  | *   buffer pointer should be prepared for writing of next sampling results, | 
|  | *   the "repeat_sampling" parameter indicates if the results should be written | 
|  | *   in the same place as before (when true) or as consecutive ones (otherwise). | 
|  | */ | 
|  | static void adc_context_start_sampling(struct adc_context *ctx); | 
|  | static void adc_context_update_buffer_pointer(struct adc_context *ctx, | 
|  | bool repeat_sampling); | 
|  | /* | 
|  | * If a given driver uses some dedicated hardware timer to trigger consecutive | 
|  | * samplings, it should implement also the following two functions. Otherwise, | 
|  | * it should define the ADC_CONTEXT_USES_KERNEL_TIMER macro to enable parts of | 
|  | * this module that utilize a standard kernel timer. | 
|  | */ | 
|  | static void adc_context_enable_timer(struct adc_context *ctx); | 
|  | static void adc_context_disable_timer(struct adc_context *ctx); | 
|  |  | 
|  |  | 
|  | struct adc_context { | 
|  | atomic_t sampling_requested; | 
|  | #ifdef ADC_CONTEXT_USES_KERNEL_TIMER | 
|  | struct k_timer timer; | 
|  | #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */ | 
|  |  | 
|  | struct k_sem lock; | 
|  | struct k_sem sync; | 
|  | int status; | 
|  |  | 
|  | #ifdef CONFIG_ADC_ASYNC | 
|  | struct k_poll_signal *signal; | 
|  | bool asynchronous; | 
|  | #endif /* CONFIG_ADC_ASYNC */ | 
|  |  | 
|  | struct adc_sequence sequence; | 
|  | struct adc_sequence_options options; | 
|  | uint16_t sampling_index; | 
|  | }; | 
|  |  | 
|  | #ifdef ADC_CONTEXT_USES_KERNEL_TIMER | 
|  | #define ADC_CONTEXT_INIT_TIMER(_data, _ctx_name) \ | 
|  | ._ctx_name.timer = Z_TIMER_INITIALIZER(_data._ctx_name.timer, \ | 
|  | adc_context_on_timer_expired, \ | 
|  | NULL) | 
|  | #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */ | 
|  |  | 
|  | #define ADC_CONTEXT_INIT_LOCK(_data, _ctx_name) \ | 
|  | ._ctx_name.lock = Z_SEM_INITIALIZER(_data._ctx_name.lock, 0, 1) | 
|  |  | 
|  | #define ADC_CONTEXT_INIT_SYNC(_data, _ctx_name) \ | 
|  | ._ctx_name.sync = Z_SEM_INITIALIZER(_data._ctx_name.sync, 0, 1) | 
|  |  | 
|  |  | 
|  | static inline void adc_context_request_next_sampling(struct adc_context *ctx) | 
|  | { | 
|  | if (atomic_inc(&ctx->sampling_requested) == 0) { | 
|  | adc_context_start_sampling(ctx); | 
|  | } else { | 
|  | /* | 
|  | * If a sampling was already requested and was not finished yet, | 
|  | * do not start another one from here, this will be done from | 
|  | * adc_context_on_sampling_done() after the current sampling is | 
|  | * complete. Instead, note this fact, and inform the user about | 
|  | * it after the sequence is done. | 
|  | */ | 
|  | ctx->status = -EBUSY; | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef ADC_CONTEXT_USES_KERNEL_TIMER | 
|  | static inline void adc_context_enable_timer(struct adc_context *ctx) | 
|  | { | 
|  | k_timer_start(&ctx->timer, K_NO_WAIT, K_USEC(ctx->options.interval_us)); | 
|  | } | 
|  |  | 
|  | static inline void adc_context_disable_timer(struct adc_context *ctx) | 
|  | { | 
|  | k_timer_stop(&ctx->timer); | 
|  | } | 
|  |  | 
|  | static void adc_context_on_timer_expired(struct k_timer *timer_id) | 
|  | { | 
|  | struct adc_context *ctx = | 
|  | CONTAINER_OF(timer_id, struct adc_context, timer); | 
|  |  | 
|  | adc_context_request_next_sampling(ctx); | 
|  | } | 
|  | #endif /* ADC_CONTEXT_USES_KERNEL_TIMER */ | 
|  |  | 
|  |  | 
|  | static inline void adc_context_lock(struct adc_context *ctx, | 
|  | bool asynchronous, | 
|  | struct k_poll_signal *signal) | 
|  | { | 
|  | k_sem_take(&ctx->lock, K_FOREVER); | 
|  |  | 
|  | #ifdef CONFIG_ADC_ASYNC | 
|  | ctx->asynchronous = asynchronous; | 
|  | ctx->signal = signal; | 
|  | #endif /* CONFIG_ADC_ASYNC */ | 
|  | } | 
|  |  | 
|  | static inline void adc_context_release(struct adc_context *ctx, int status) | 
|  | { | 
|  | #ifdef CONFIG_ADC_ASYNC | 
|  | if (ctx->asynchronous && (status == 0)) { | 
|  | return; | 
|  | } | 
|  | #endif /* CONFIG_ADC_ASYNC */ | 
|  |  | 
|  | k_sem_give(&ctx->lock); | 
|  | } | 
|  |  | 
|  | static inline void adc_context_unlock_unconditionally(struct adc_context *ctx) | 
|  | { | 
|  | if (!k_sem_count_get(&ctx->lock)) { | 
|  | k_sem_give(&ctx->lock); | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline int adc_context_wait_for_completion(struct adc_context *ctx) | 
|  | { | 
|  | #ifdef CONFIG_ADC_ASYNC | 
|  | if (ctx->asynchronous) { | 
|  | return 0; | 
|  | } | 
|  | #endif /* CONFIG_ADC_ASYNC */ | 
|  |  | 
|  | k_sem_take(&ctx->sync, K_FOREVER); | 
|  | return ctx->status; | 
|  | } | 
|  |  | 
|  | static inline void adc_context_complete(struct adc_context *ctx, int status) | 
|  | { | 
|  | #ifdef CONFIG_ADC_ASYNC | 
|  | if (ctx->asynchronous) { | 
|  | if (ctx->signal) { | 
|  | k_poll_signal_raise(ctx->signal, status); | 
|  | } | 
|  |  | 
|  | k_sem_give(&ctx->lock); | 
|  | return; | 
|  | } | 
|  | #endif /* CONFIG_ADC_ASYNC */ | 
|  |  | 
|  | /* | 
|  | * Override the status only when an error is signaled to this function. | 
|  | * Please note that adc_context_request_next_sampling() might have set | 
|  | * this field. | 
|  | */ | 
|  | if (status != 0) { | 
|  | ctx->status = status; | 
|  | } | 
|  | k_sem_give(&ctx->sync); | 
|  | } | 
|  |  | 
|  | static inline void adc_context_start_read(struct adc_context *ctx, | 
|  | const struct adc_sequence *sequence) | 
|  | { | 
|  | ctx->sequence = *sequence; | 
|  | ctx->status = 0; | 
|  |  | 
|  | if (sequence->options) { | 
|  | ctx->options = *sequence->options; | 
|  | ctx->sequence.options = &ctx->options; | 
|  | ctx->sampling_index = 0U; | 
|  |  | 
|  | if (ctx->options.interval_us != 0U) { | 
|  | atomic_set(&ctx->sampling_requested, 0); | 
|  | adc_context_enable_timer(ctx); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | adc_context_start_sampling(ctx); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This function should be called after a sampling (of one or more channels, | 
|  | * depending on the realized sequence) is done. It calls the defined callback | 
|  | * function if required and takes further actions accordingly. | 
|  | */ | 
|  | static inline void adc_context_on_sampling_done(struct adc_context *ctx, | 
|  | const struct device *dev) | 
|  | { | 
|  | if (ctx->sequence.options) { | 
|  | adc_sequence_callback callback = ctx->options.callback; | 
|  | enum adc_action action; | 
|  | bool finish = false; | 
|  | bool repeat = false; | 
|  |  | 
|  | if (callback) { | 
|  | action = callback(dev, | 
|  | &ctx->sequence, | 
|  | ctx->sampling_index); | 
|  | } else { | 
|  | action = ADC_ACTION_CONTINUE; | 
|  | } | 
|  |  | 
|  | switch (action) { | 
|  | case ADC_ACTION_REPEAT: | 
|  | repeat = true; | 
|  | break; | 
|  | case ADC_ACTION_FINISH: | 
|  | finish = true; | 
|  | break; | 
|  | default: /* ADC_ACTION_CONTINUE */ | 
|  | if (ctx->sampling_index < | 
|  | ctx->options.extra_samplings) { | 
|  | ++ctx->sampling_index; | 
|  | } else { | 
|  | finish = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!finish) { | 
|  | adc_context_update_buffer_pointer(ctx, repeat); | 
|  |  | 
|  | /* | 
|  | * Immediately start the next sampling if working with | 
|  | * a zero interval or if the timer expired again while | 
|  | * the current sampling was in progress. | 
|  | */ | 
|  | if (ctx->options.interval_us == 0U) { | 
|  | adc_context_start_sampling(ctx); | 
|  | } else if (atomic_dec(&ctx->sampling_requested) > 1) { | 
|  | adc_context_start_sampling(ctx); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (ctx->options.interval_us != 0U) { | 
|  | adc_context_disable_timer(ctx); | 
|  | } | 
|  | } | 
|  |  | 
|  | adc_context_complete(ctx, 0); | 
|  | } | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif /* ZEPHYR_DRIVERS_ADC_ADC_CONTEXT_H_ */ |