blob: 4224f42e166298ccf7be5cd116602d9c2ab78876 [file] [log] [blame]
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +02001/*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#define ADC_CONTEXT_USES_KERNEL_TIMER
8#include "adc_context.h"
9#include <nrfx_adc.h>
10
Anas Nashif9a8567f2018-09-17 12:00:47 -050011#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL
12#include <logging/log.h>
13LOG_MODULE_REGISTER(adc_mcux_adc16);
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +020014
15struct driver_data {
16 struct adc_context ctx;
17
18 nrf_adc_value_t *buffer;
19 u8_t active_channels;
20};
21
22static struct driver_data m_data = {
23 ADC_CONTEXT_INIT_TIMER(m_data, ctx),
24 ADC_CONTEXT_INIT_LOCK(m_data, ctx),
25 ADC_CONTEXT_INIT_SYNC(m_data, ctx),
26};
27
28static nrfx_adc_channel_t m_channels[CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT];
29
30
31/* Implementation of the ADC driver API function: adc_channel_setup. */
32static int adc_nrfx_channel_setup(struct device *dev,
33 const struct adc_channel_cfg *channel_cfg)
34{
35 u8_t channel_id = channel_cfg->channel_id;
36 nrf_adc_config_t *config = &m_channels[channel_id].config;
37
38 if (channel_id >= CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT) {
39 return -EINVAL;
40 }
41
42 if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
Anas Nashif9a8567f2018-09-17 12:00:47 -050043 LOG_ERR("Selected ADC acquisition time is not valid");
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +020044 return -EINVAL;
45 }
46
47 if (channel_cfg->differential) {
Anas Nashif9a8567f2018-09-17 12:00:47 -050048 LOG_ERR("Differential channels are not supported");
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +020049 return -EINVAL;
50 }
51
52 switch (channel_cfg->gain) {
53 case ADC_GAIN_1_3:
54 config->scaling = NRF_ADC_CONFIG_SCALING_INPUT_ONE_THIRD;
55 break;
56 case ADC_GAIN_2_3:
57 config->scaling = NRF_ADC_CONFIG_SCALING_INPUT_TWO_THIRDS;
58 break;
59 case ADC_GAIN_1:
60 config->scaling = NRF_ADC_CONFIG_SCALING_INPUT_FULL_SCALE;
61 break;
62 default:
Anas Nashif9a8567f2018-09-17 12:00:47 -050063 LOG_ERR("Selected ADC gain is not valid");
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +020064 return -EINVAL;
65 }
66
67 switch (channel_cfg->reference) {
68 case ADC_REF_INTERNAL:
69 config->reference = NRF_ADC_CONFIG_REF_VBG;
70 config->extref = NRF_ADC_CONFIG_EXTREFSEL_NONE;
71 break;
72 case ADC_REF_VDD_1_2:
73 config->reference = NRF_ADC_CONFIG_REF_SUPPLY_ONE_HALF;
74 config->extref = NRF_ADC_CONFIG_EXTREFSEL_NONE;
75 break;
76 case ADC_REF_VDD_1_3:
77 config->reference = NRF_ADC_CONFIG_REF_SUPPLY_ONE_THIRD;
78 config->extref = NRF_ADC_CONFIG_EXTREFSEL_NONE;
79 break;
80 case ADC_REF_EXTERNAL0:
81 config->reference = NRF_ADC_CONFIG_REF_EXT;
82 config->extref = NRF_ADC_CONFIG_EXTREFSEL_AREF0;
83 break;
84 case ADC_REF_EXTERNAL1:
85 config->reference = NRF_ADC_CONFIG_REF_EXT;
86 config->extref = NRF_ADC_CONFIG_EXTREFSEL_AREF1;
87 break;
88 default:
Anas Nashif9a8567f2018-09-17 12:00:47 -050089 LOG_ERR("Selected ADC reference is not valid");
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +020090 return -EINVAL;
91 }
92
93 config->input = channel_cfg->input_positive;
94
95 config->resolution = NRF_ADC_CONFIG_RES_8BIT;
96
97 return 0;
98}
99
100static void adc_context_start_sampling(struct adc_context *ctx)
101{
102 ARG_UNUSED(ctx);
103
104 nrfx_adc_buffer_convert(m_data.buffer, m_data.active_channels);
105 nrfx_adc_sample();
106}
107
108static void adc_context_update_buffer_pointer(struct adc_context *ctx,
109 bool repeat)
110{
111 ARG_UNUSED(ctx);
112
113 if (!repeat) {
114 m_data.buffer += m_data.active_channels;
115 }
116}
117
118static int check_buffer_size(const struct adc_sequence *sequence,
119 u8_t active_channels)
120{
121 size_t needed_buffer_size;
122
123 needed_buffer_size = active_channels * sizeof(nrf_adc_value_t);
124 if (sequence->options) {
125 needed_buffer_size *= (1 + sequence->options->extra_samplings);
126 }
127
128 if (sequence->buffer_size < needed_buffer_size) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500129 LOG_ERR("Provided buffer is too small (%u/%u)",
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +0200130 sequence->buffer_size, needed_buffer_size);
131 return -ENOMEM;
132 }
133
134 return 0;
135}
136
137static int start_read(struct device *dev, const struct adc_sequence *sequence)
138{
139 int error = 0;
140 u32_t selected_channels = sequence->channels;
141 u8_t active_channels;
142 u8_t channel_id;
143 nrf_adc_config_resolution_t nrf_resolution;
144
145 /* Signal an error if channel selection is invalid (no channels or
146 * a non-existing one is selected).
147 */
148 if (!selected_channels ||
149 (selected_channels &
150 ~BIT_MASK(CONFIG_ADC_NRFX_ADC_CHANNEL_COUNT))) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500151 LOG_ERR("Invalid selection of channels");
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +0200152 return -EINVAL;
153 }
154
155 if (sequence->oversampling != 0) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500156 LOG_ERR("Oversampling is not supported");
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +0200157 return -EINVAL;
158 }
159
160 switch (sequence->resolution) {
161 case 8:
162 nrf_resolution = NRF_ADC_CONFIG_RES_8BIT;
163 break;
164 case 9:
165 nrf_resolution = NRF_ADC_CONFIG_RES_9BIT;
166 break;
167 case 10:
168 nrf_resolution = NRF_ADC_CONFIG_RES_10BIT;
169 break;
170 default:
Anas Nashif9a8567f2018-09-17 12:00:47 -0500171 LOG_ERR("ADC resolution value %d is not valid",
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +0200172 sequence->resolution);
173 return -EINVAL;
174 }
175
176 active_channels = 0;
177 nrfx_adc_all_channels_disable();
178
179 /* Enable the channels selected for the pointed sequence.
180 */
181 channel_id = 0;
182 while (selected_channels) {
183 if (selected_channels & BIT(0)) {
184 /* The nrfx driver requires setting the resolution
185 * for each enabled channel individually.
186 */
187 m_channels[channel_id].config.resolution =
188 nrf_resolution;
189 nrfx_adc_channel_enable(&m_channels[channel_id]);
190 ++active_channels;
191 }
192 selected_channels >>= 1;
193 ++channel_id;
194 }
195
196 error = check_buffer_size(sequence, active_channels);
197 if (error) {
198 return error;
199 }
200
201 m_data.buffer = sequence->buffer;
202 m_data.active_channels = active_channels;
203
204 adc_context_start_read(&m_data.ctx, sequence);
205
206 if (!error) {
207 error = adc_context_wait_for_completion(&m_data.ctx);
208 adc_context_release(&m_data.ctx, error);
209 }
210
211 return error;
212}
213
214/* Implementation of the ADC driver API function: adc_read. */
215static int adc_nrfx_read(struct device *dev,
216 const struct adc_sequence *sequence)
217{
218 adc_context_lock(&m_data.ctx, false, NULL);
219 return start_read(dev, sequence);
220}
221
222#ifdef CONFIG_ADC_ASYNC
223/* Implementation of the ADC driver API function: adc_read_sync. */
224static int adc_nrfx_read_async(struct device *dev,
225 const struct adc_sequence *sequence,
226 struct k_poll_signal *async)
227{
228 adc_context_lock(&m_data.ctx, true, async);
229 return start_read(dev, sequence);
230}
231#endif
232
233DEVICE_DECLARE(adc_0);
234
235static void event_handler(const nrfx_adc_evt_t *p_event)
236{
237 struct device *dev = DEVICE_GET(adc_0);
238
239 if (p_event->type == NRFX_ADC_EVT_DONE) {
240 adc_context_on_sampling_done(&m_data.ctx, dev);
241 }
242}
243
244static int init_adc(struct device *dev)
245{
246 const nrfx_adc_config_t config = NRFX_ADC_DEFAULT_CONFIG;
247
248 nrfx_err_t result = nrfx_adc_init(&config, event_handler);
249
250 if (result != NRFX_SUCCESS) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500251 LOG_ERR("Failed to initialize device: %s",
Andrzej Głąbekaad21ec2018-05-21 15:01:08 +0200252 dev->config->name);
253 return -EBUSY;
254 }
255
256 IRQ_CONNECT(CONFIG_ADC_0_IRQ, CONFIG_ADC_0_IRQ_PRI,
257 nrfx_isr, nrfx_adc_irq_handler, 0);
258
259 adc_context_unlock_unconditionally(&m_data.ctx);
260
261 return 0;
262}
263
264static const struct adc_driver_api adc_nrfx_driver_api = {
265 .channel_setup = adc_nrfx_channel_setup,
266 .read = adc_nrfx_read,
267#ifdef CONFIG_ADC_ASYNC
268 .read_async = adc_nrfx_read_async,
269#endif
270};
271
272#ifdef CONFIG_ADC_0
273DEVICE_AND_API_INIT(adc_0, CONFIG_ADC_0_NAME,
274 init_adc, NULL, NULL,
275 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
276 &adc_nrfx_driver_api);
277#endif /* CONFIG_ADC_0 */