blob: f993265bd0640ebce74cbc56206d749430af4395 [file] [log] [blame]
Jonas Pfaffb62a3532017-08-04 13:38:18 +02001/*
2 * Copyright (c) 2017 comsuisse AG
Justin Watson4e551aa2018-08-15 15:01:57 -07003 * Copyright (c) 2018 Justin Watson
Jonas Pfaffb62a3532017-08-04 13:38:18 +02004 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8/** @file
9 * @brief Atmel SAM MCU family ADC (AFEC) driver.
Justin Watson4e551aa2018-08-15 15:01:57 -070010 *
11 * This is an implementation of the Zephyr ADC driver using the SAM Analog
12 * Front-End Controller (AFEC) peripheral.
Jonas Pfaffb62a3532017-08-04 13:38:18 +020013 */
14
15#include <errno.h>
Anas Nashif5eb90ec2019-06-26 10:33:39 -040016#include <sys/__assert.h>
Anas Nashifa2fd7d72019-06-26 10:33:55 -040017#include <sys/util.h>
Jonas Pfaffb62a3532017-08-04 13:38:18 +020018#include <device.h>
19#include <init.h>
20#include <soc.h>
Anas Nashiff4709f22019-06-25 15:53:45 -040021#include <drivers/adc.h>
Jonas Pfaffb62a3532017-08-04 13:38:18 +020022
Justin Watson4e551aa2018-08-15 15:01:57 -070023#define ADC_CONTEXT_USES_KERNEL_TIMER
24#include "adc_context.h"
25
Anas Nashif9a8567f2018-09-17 12:00:47 -050026#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL
27#include <logging/log.h>
28LOG_MODULE_REGISTER(adc_sam_afec);
Jonas Pfaffb62a3532017-08-04 13:38:18 +020029
Justin Watson4e551aa2018-08-15 15:01:57 -070030#define NUM_CHANNELS 12
Jonas Pfaffb62a3532017-08-04 13:38:18 +020031
32#define CONF_ADC_PRESCALER ((SOC_ATMEL_SAM_MCK_FREQ_HZ / 15000000) - 1)
33
Justin Watson4e551aa2018-08-15 15:01:57 -070034typedef void (*cfg_func_t)(struct device *dev);
35
36struct adc_sam_data {
37 struct adc_context ctx;
38 struct device *dev;
39
40 /* Pointer to the buffer in the sequence. */
41 u16_t *buffer;
42
43 /* Pointer to the beginning of a sample. Consider the number of
44 * channels in the sequence: this buffer changes by that amount
45 * so all the channels would get repeated.
46 */
47 u16_t *repeat_buffer;
48
49 /* Bit mask of the channels to be sampled. */
50 u32_t channels;
51
52 /* Index of the channel being sampled. */
53 u8_t channel_id;
54};
55
56struct adc_sam_cfg {
57 Afec *regs;
58 cfg_func_t cfg_func;
59 u32_t periph_id;
60 struct soc_gpio_pin afec_trg_pin;
61};
62
Jonas Pfaffb62a3532017-08-04 13:38:18 +020063#define DEV_CFG(dev) \
Justin Watson4e551aa2018-08-15 15:01:57 -070064 ((const struct adc_sam_cfg *const)(dev)->config->config_info)
65
Jonas Pfaffb62a3532017-08-04 13:38:18 +020066#define DEV_DATA(dev) \
Justin Watson4e551aa2018-08-15 15:01:57 -070067 ((struct adc_sam_data *)(dev)->driver_data)
Jonas Pfaffb62a3532017-08-04 13:38:18 +020068
Justin Watson4e551aa2018-08-15 15:01:57 -070069static int adc_sam_channel_setup(struct device *dev,
70 const struct adc_channel_cfg *channel_cfg)
Jonas Pfaffb62a3532017-08-04 13:38:18 +020071{
Justin Watson4e551aa2018-08-15 15:01:57 -070072 const struct adc_sam_cfg * const cfg = DEV_CFG(dev);
73 Afec *const afec = cfg->regs;
Jonas Pfaffb62a3532017-08-04 13:38:18 +020074
Justin Watson4e551aa2018-08-15 15:01:57 -070075 u8_t channel_id = channel_cfg->channel_id;
Jonas Pfaffb62a3532017-08-04 13:38:18 +020076
Justin Watson4e551aa2018-08-15 15:01:57 -070077 /* Clear the gain bits for the channel. */
Patrik Flykt24d71432019-03-26 19:57:45 -060078 afec->AFEC_CGR &= ~(3 << channel_id * 2U);
Jonas Pfaffb62a3532017-08-04 13:38:18 +020079
Justin Watson4e551aa2018-08-15 15:01:57 -070080 switch (channel_cfg->gain) {
81 case ADC_GAIN_1:
82 /* A value of 0 in this register is a gain of 1. */
83 break;
84 case ADC_GAIN_1_2:
Patrik Flykt24d71432019-03-26 19:57:45 -060085 afec->AFEC_CGR |= (1 << (channel_id * 2U));
Justin Watson4e551aa2018-08-15 15:01:57 -070086 break;
87 case ADC_GAIN_1_4:
Patrik Flykt24d71432019-03-26 19:57:45 -060088 afec->AFEC_CGR |= (2 << (channel_id * 2U));
Justin Watson4e551aa2018-08-15 15:01:57 -070089 break;
90 default:
Anas Nashif9a8567f2018-09-17 12:00:47 -050091 LOG_ERR("Selected ADC gain is not valid");
Justin Watson4e551aa2018-08-15 15:01:57 -070092 return -EINVAL;
Jonas Pfaffb62a3532017-08-04 13:38:18 +020093 }
94
Justin Watson4e551aa2018-08-15 15:01:57 -070095 if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
Anas Nashif9a8567f2018-09-17 12:00:47 -050096 LOG_ERR("Selected ADC acquisition time is not valid");
Justin Watson4e551aa2018-08-15 15:01:57 -070097 return -EINVAL;
Jonas Pfaffb62a3532017-08-04 13:38:18 +020098 }
99
Justin Watson4e551aa2018-08-15 15:01:57 -0700100 if (channel_cfg->reference != ADC_REF_EXTERNAL0) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500101 LOG_ERR("Selected reference is not valid");
Justin Watson4e551aa2018-08-15 15:01:57 -0700102 return -EINVAL;
103 }
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200104
Justin Watson4e551aa2018-08-15 15:01:57 -0700105 if (channel_cfg->differential) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500106 LOG_ERR("Differential input is not supported");
Justin Watson4e551aa2018-08-15 15:01:57 -0700107 return -EINVAL;
108 }
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200109
Justin Watson4e551aa2018-08-15 15:01:57 -0700110 /* Set single ended channels to unsigned and differential channels
111 * to signed conversions.
112 */
113 afec->AFEC_EMR &= ~(AFEC_EMR_SIGNMODE(
114 AFEC_EMR_SIGNMODE_SE_UNSG_DF_SIGN_Val));
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200115
116 return 0;
117}
118
Justin Watson4e551aa2018-08-15 15:01:57 -0700119static void adc_sam_start_conversion(struct device *dev)
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200120{
Justin Watson4e551aa2018-08-15 15:01:57 -0700121 const struct adc_sam_cfg *const cfg = DEV_CFG(dev);
122 struct adc_sam_data *data = DEV_DATA(dev);
123 Afec *const afec = cfg->regs;
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200124
Justin Watson4e551aa2018-08-15 15:01:57 -0700125 data->channel_id = find_lsb_set(data->channels) - 1;
126
Anas Nashif9a8567f2018-09-17 12:00:47 -0500127 LOG_DBG("Starting channel %d", data->channel_id);
Justin Watson4e551aa2018-08-15 15:01:57 -0700128
129 /* Disable all channels. */
130 afec->AFEC_CHDR = 0xfff;
131 afec->AFEC_IDR = 0xfff;
132
133 /* Enable the ADC channel. This also enables/selects the channel pin as
134 * an input to the AFEC (50.5.1 SAM E70 datasheet).
135 */
136 afec->AFEC_CHER = (1 << data->channel_id);
137
138 /* Enable the interrupt for the channel. */
139 afec->AFEC_IER = (1 << data->channel_id);
140
141 /* Start the conversions. */
142 afec->AFEC_CR = AFEC_CR_START;
143}
144
145/**
146 * This is only called once at the beginning of all the conversions,
147 * all channels as a group.
148 */
149static void adc_context_start_sampling(struct adc_context *ctx)
150{
151 struct adc_sam_data *data = CONTAINER_OF(ctx, struct adc_sam_data, ctx);
152
Andrew Boie1efaae52019-03-29 17:24:28 -0700153 data->channels = ctx->sequence.channels;
Justin Watson4e551aa2018-08-15 15:01:57 -0700154
155 adc_sam_start_conversion(data->dev);
156}
157
158static void adc_context_update_buffer_pointer(struct adc_context *ctx,
159 bool repeat_sampling)
160{
161 struct adc_sam_data *data = CONTAINER_OF(ctx, struct adc_sam_data, ctx);
162
163 if (repeat_sampling) {
164 data->buffer = data->repeat_buffer;
165 }
166}
167
168static int check_buffer_size(const struct adc_sequence *sequence,
169 u8_t active_channels)
170{
171 size_t needed_buffer_size;
172 needed_buffer_size = active_channels * sizeof(u16_t);
173 if (sequence->options) {
174 needed_buffer_size *= (1 + sequence->options->extra_samplings);
175 }
176 if (sequence->buffer_size < needed_buffer_size) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500177 LOG_ERR("Provided buffer is too small (%u/%u)",
Justin Watson4e551aa2018-08-15 15:01:57 -0700178 sequence->buffer_size, needed_buffer_size);
179 return -ENOMEM;
180 }
181 return 0;
182}
183
184static int start_read(struct device *dev, const struct adc_sequence *sequence)
185{
186 struct adc_sam_data *data = DEV_DATA(dev);
187 int error = 0;
188 u32_t channels = sequence->channels;
189
Patrik Flykt8ff96b52018-11-29 11:12:22 -0800190 data->channels = 0U;
Justin Watson4e551aa2018-08-15 15:01:57 -0700191
192 /* Signal an error if the channel selection is invalid (no channels or
193 * a non-existing one is selected).
194 */
Patrik Flykt24d71432019-03-26 19:57:45 -0600195 if (channels == 0U ||
196 (channels & (~0UL << NUM_CHANNELS))) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500197 LOG_ERR("Invalid selection of channels");
Justin Watson4e551aa2018-08-15 15:01:57 -0700198 return -EINVAL;
199 }
200
Patrik Flykt24d71432019-03-26 19:57:45 -0600201 if (sequence->oversampling != 0U) {
Anas Nashif9a8567f2018-09-17 12:00:47 -0500202 LOG_ERR("Oversampling is not supported");
Justin Watson4e551aa2018-08-15 15:01:57 -0700203 return -EINVAL;
204 }
205
Patrik Flykt24d71432019-03-26 19:57:45 -0600206 if (sequence->resolution != 12U) {
Justin Watson4e551aa2018-08-15 15:01:57 -0700207 /* TODO JKW: Support the Enhanced Resolution Mode 50.6.3 page
208 * 1544.
209 */
Anas Nashif9a8567f2018-09-17 12:00:47 -0500210 LOG_ERR("ADC resolution value %d is not valid",
Justin Watson4e551aa2018-08-15 15:01:57 -0700211 sequence->resolution);
212 return -EINVAL;
213 }
214
Patrik Flykt8ff96b52018-11-29 11:12:22 -0800215 u8_t num_active_channels = 0U;
216 u8_t channel = 0U;
Justin Watson4e551aa2018-08-15 15:01:57 -0700217
218 while (channels > 0) {
219 if (channels & 1) {
220 ++num_active_channels;
221 }
222 channels >>= 1;
223 ++channel;
224 }
225
226 error = check_buffer_size(sequence, num_active_channels);
227 if (error) {
228 return error;
229 }
230
231 /* In the context you have a pointer to the adc_sam_data structure
232 * only.
233 */
234 data->buffer = sequence->buffer;
235 data->repeat_buffer = sequence->buffer;
236
237 /* At this point we allow the scheduler to do other things while
238 * we wait for the conversions to complete. This is provided by the
239 * adc_context functions. However, the caller of this function is
240 * blocked until the results are in.
241 */
242 adc_context_start_read(&data->ctx, sequence);
243
244 error = adc_context_wait_for_completion(&data->ctx);
Justin Watson4e551aa2018-08-15 15:01:57 -0700245 return error;
246}
247
248static int adc_sam_read(struct device *dev,
249 const struct adc_sequence *sequence)
250{
251 struct adc_sam_data *data = DEV_DATA(dev);
Andrzej Głąbek0906a512018-12-17 08:04:10 +0100252 int error;
Justin Watson4e551aa2018-08-15 15:01:57 -0700253
254 adc_context_lock(&data->ctx, false, NULL);
Andrzej Głąbek0906a512018-12-17 08:04:10 +0100255 error = start_read(dev, sequence);
256 adc_context_release(&data->ctx, error);
257
258 return error;
Justin Watson4e551aa2018-08-15 15:01:57 -0700259}
260
261static int adc_sam_init(struct device *dev)
262{
263 const struct adc_sam_cfg *const cfg = DEV_CFG(dev);
264 struct adc_sam_data *data = DEV_DATA(dev);
265 Afec *const afec = cfg->regs;
266
267 /* Reset the AFEC. */
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200268 afec->AFEC_CR = AFEC_CR_SWRST;
269
270 afec->AFEC_MR = AFEC_MR_TRGEN_DIS
271 | AFEC_MR_SLEEP_NORMAL
272 | AFEC_MR_FWUP_OFF
273 | AFEC_MR_FREERUN_OFF
274 | AFEC_MR_PRESCAL(CONF_ADC_PRESCALER)
275 | AFEC_MR_STARTUP_SUT96
276 | AFEC_MR_ONE
277 | AFEC_MR_USEQ_NUM_ORDER;
278
Justin Watson4e551aa2018-08-15 15:01:57 -0700279 /* Set all channels CM voltage to Vrefp/2 (512). */
280 for (int i = 0; i < NUM_CHANNELS; i++) {
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200281 afec->AFEC_CSELR = i;
282 afec->AFEC_COCR = 512;
283 }
284
Justin Watson4e551aa2018-08-15 15:01:57 -0700285 /* Enable PGA and Current Bias. */
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200286 afec->AFEC_ACR = AFEC_ACR_PGA0EN
287 | AFEC_ACR_PGA1EN
288 | AFEC_ACR_IBCTL(1);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200289
Justin Watson4e551aa2018-08-15 15:01:57 -0700290 soc_pmc_peripheral_enable(cfg->periph_id);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200291
Justin Watson4e551aa2018-08-15 15:01:57 -0700292 cfg->cfg_func(dev);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200293
Justin Watson4e551aa2018-08-15 15:01:57 -0700294 data->dev = dev;
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200295
Justin Watson4e551aa2018-08-15 15:01:57 -0700296 adc_context_unlock_unconditionally(&data->ctx);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200297
298 return 0;
299}
300
Justin Watson4e551aa2018-08-15 15:01:57 -0700301#ifdef CONFIG_ADC_ASYNC
302static int adc_sam_read_async(struct device *dev,
303 const struct adc_sequence *sequence,
304 struct k_poll_signal *async)
305{
306 struct adc_sam_data *data = DEV_DATA(dev);
Andrzej Głąbek0906a512018-12-17 08:04:10 +0100307 int error;
Justin Watson4e551aa2018-08-15 15:01:57 -0700308
309 adc_context_lock(&data->ctx, true, async);
Andrzej Głąbek0906a512018-12-17 08:04:10 +0100310 error = start_read(dev, sequence);
311 adc_context_release(&data->ctx, error);
312
313 return error;
Justin Watson4e551aa2018-08-15 15:01:57 -0700314}
315#endif
316
317static const struct adc_driver_api adc_sam_api = {
318 .channel_setup = adc_sam_channel_setup,
319 .read = adc_sam_read,
320#ifdef CONFIG_ADC_ASYNC
321 .read_async = adc_sam_read_async,
322#endif
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200323};
324
Justin Watson4e551aa2018-08-15 15:01:57 -0700325static void adc_sam_isr(void *arg)
326{
327 struct device *dev = (struct device *)arg;
328 struct adc_sam_data *data = DEV_DATA(dev);
329 const struct adc_sam_cfg *const cfg = DEV_CFG(dev);
330 Afec *const afec = cfg->regs;
331 u16_t result;
332
333 afec->AFEC_CHDR |= BIT(data->channel_id);
334 afec->AFEC_IDR |= BIT(data->channel_id);
335
336 afec->AFEC_CSELR = AFEC_CSELR_CSEL(data->channel_id);
337 result = (u16_t)(afec->AFEC_CDR);
338
339 *data->buffer++ = result;
340 data->channels &= ~BIT(data->channel_id);
341
342 if (data->channels) {
343 adc_sam_start_conversion(dev);
344 } else {
345 /* Called once all conversions have completed.*/
346 adc_context_on_sampling_done(&data->ctx, dev);
347 }
348}
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200349
350#ifdef CONFIG_ADC_0
Justin Watson4e551aa2018-08-15 15:01:57 -0700351static void adc0_sam_cfg_func(struct device *dev);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200352
Justin Watson4e551aa2018-08-15 15:01:57 -0700353static const struct adc_sam_cfg adc0_sam_cfg = {
Andrzej Głąbek20202902018-11-13 15:15:23 +0100354 .regs = (Afec *)DT_ADC_0_BASE_ADDRESS,
Justin Watson4e551aa2018-08-15 15:01:57 -0700355 .cfg_func = adc0_sam_cfg_func,
Andrzej Głąbek20202902018-11-13 15:15:23 +0100356 .periph_id = DT_ADC_0_PERIPHERAL_ID,
Justin Watson4e551aa2018-08-15 15:01:57 -0700357 .afec_trg_pin = PIN_AFE0_ADTRG,
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200358};
359
Justin Watson4e551aa2018-08-15 15:01:57 -0700360static struct adc_sam_data adc0_sam_data = {
361 ADC_CONTEXT_INIT_TIMER(adc0_sam_data, ctx),
362 ADC_CONTEXT_INIT_LOCK(adc0_sam_data, ctx),
363 ADC_CONTEXT_INIT_SYNC(adc0_sam_data, ctx),
364};
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200365
Kumar Galac2569492018-11-21 10:29:57 -0600366DEVICE_AND_API_INIT(adc0_sam, DT_ADC_0_NAME, adc_sam_init,
Justin Watson4e551aa2018-08-15 15:01:57 -0700367 &adc0_sam_data, &adc0_sam_cfg, POST_KERNEL,
368 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &adc_sam_api);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200369
Justin Watson4e551aa2018-08-15 15:01:57 -0700370static void adc0_sam_cfg_func(struct device *dev)
371{
Kumar Galad4983db2018-11-21 10:24:00 -0600372 IRQ_CONNECT(DT_ADC_0_IRQ, DT_ADC_0_IRQ_PRI, adc_sam_isr,
Justin Watson4e551aa2018-08-15 15:01:57 -0700373 DEVICE_GET(adc0_sam), 0);
Andrzej Głąbek20202902018-11-13 15:15:23 +0100374 irq_enable(DT_ADC_0_IRQ);
Justin Watson4e551aa2018-08-15 15:01:57 -0700375}
376
377#endif /* CONFIG_ADC_0 */
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200378
379#ifdef CONFIG_ADC_1
Justin Watson4e551aa2018-08-15 15:01:57 -0700380static void adc1_sam_cfg_func(struct device *dev);
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200381
Justin Watson4e551aa2018-08-15 15:01:57 -0700382static const struct adc_sam_cfg adc1_sam_cfg = {
Andrzej Głąbek20202902018-11-13 15:15:23 +0100383 .regs = (Afec *)DT_ADC_1_BASE_ADDRESS,
Justin Watson4e551aa2018-08-15 15:01:57 -0700384 .cfg_func = adc1_sam_cfg_func,
Andrzej Głąbek20202902018-11-13 15:15:23 +0100385 .periph_id = DT_ADC_1_PERIPHERAL_ID,
Justin Watson4e551aa2018-08-15 15:01:57 -0700386 .afec_trg_pin = PIN_AFE1_ADTRG,
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200387};
388
Justin Watson4e551aa2018-08-15 15:01:57 -0700389static struct adc_sam_data adc1_sam_data = {
390 ADC_CONTEXT_INIT_TIMER(adc1_sam_data, ctx),
391 ADC_CONTEXT_INIT_LOCK(adc1_sam_data, ctx),
392 ADC_CONTEXT_INIT_SYNC(adc1_sam_data, ctx),
393};
Jonas Pfaffb62a3532017-08-04 13:38:18 +0200394
Kumar Galac2569492018-11-21 10:29:57 -0600395DEVICE_AND_API_INIT(adc1_sam, DT_ADC_1_NAME, adc_sam_init,
Justin Watson4e551aa2018-08-15 15:01:57 -0700396 &adc1_sam_data, &adc1_sam_cfg, POST_KERNEL,
397 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &adc_sam_api);
398
399static void adc1_sam_cfg_func(struct device *dev)
400{
Kumar Galad4983db2018-11-21 10:24:00 -0600401 IRQ_CONNECT(DT_ADC_1_IRQ, DT_ADC_1_IRQ_PRI, adc_sam_isr,
Justin Watson4e551aa2018-08-15 15:01:57 -0700402 DEVICE_GET(adc1_sam), 0);
Andrzej Głąbek20202902018-11-13 15:15:23 +0100403 irq_enable(DT_ADC_1_IRQ);
Justin Watson4e551aa2018-08-15 15:01:57 -0700404}
405
406#endif /* CONFIG_ADC_1 */