2019-02-24 15:47:57 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2020-04-28 23:35:58 +02:00
|
|
|
#define DT_DRV_COMPAT atmel_sam0_adc
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#include <soc.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/drivers/adc.h>
|
|
|
|
#include <zephyr/drivers/pinctrl.h>
|
2019-02-24 15:47:57 +01:00
|
|
|
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/logging/log.h>
|
2022-10-17 10:24:11 +02:00
|
|
|
#include <zephyr/irq.h>
|
2019-02-24 15:47:57 +01:00
|
|
|
LOG_MODULE_REGISTER(adc_sam0, CONFIG_ADC_LOG_LEVEL);
|
|
|
|
|
|
|
|
#define ADC_CONTEXT_USES_KERNEL_TIMER
|
|
|
|
#include "adc_context.h"
|
|
|
|
|
|
|
|
#if defined(CONFIG_SOC_SERIES_SAMD21) || defined(CONFIG_SOC_SERIES_SAMR21) || \
|
|
|
|
defined(CONFIG_SOC_SERIES_SAMD20)
|
|
|
|
/*
|
|
|
|
* SAMD21 Manual 33.6.2.1: The first conversion after changing the reference
|
|
|
|
* is invalid, so we have to discard it.
|
|
|
|
*/
|
|
|
|
#define ADC_SAM0_REFERENCE_GLITCH 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct adc_sam0_data {
|
|
|
|
struct adc_context ctx;
|
2020-04-30 20:33:38 +02:00
|
|
|
const struct device *dev;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t *buffer;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Saved initial start, so we can reset the advances we've done
|
|
|
|
* if required
|
|
|
|
*/
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t *repeat_buffer;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#ifdef ADC_SAM0_REFERENCE_GLITCH
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t reference_changed;
|
2019-02-24 15:47:57 +01:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
struct adc_sam0_cfg {
|
|
|
|
Adc *regs;
|
2022-03-13 17:17:01 +01:00
|
|
|
const struct pinctrl_dev_config *pcfg;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#ifdef MCLK
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t mclk_mask;
|
|
|
|
uint32_t gclk_mask;
|
|
|
|
uint16_t gclk_id;
|
2019-02-24 15:47:57 +01:00
|
|
|
#else
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t gclk;
|
2019-02-24 15:47:57 +01:00
|
|
|
#endif
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t freq;
|
|
|
|
uint16_t prescaler;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
void (*config_func)(const struct device *dev);
|
2019-02-24 15:47:57 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static void wait_synchronization(Adc *const adc)
|
|
|
|
{
|
2022-01-05 01:08:52 +01:00
|
|
|
while ((ADC_SYNC(adc) & ADC_SYNC_MASK) != 0) {
|
2019-02-24 15:47:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int adc_sam0_acquisition_to_clocks(const struct device *dev,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t acquisition_time)
|
2019-02-24 15:47:57 +01:00
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint64_t scaled_acq;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
switch (ADC_ACQ_TIME_UNIT(acquisition_time)) {
|
|
|
|
case ADC_ACQ_TIME_TICKS:
|
|
|
|
if (ADC_ACQ_TIME_VALUE(acquisition_time) > 64U) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (int)ADC_ACQ_TIME_VALUE(acquisition_time) - 1;
|
|
|
|
case ADC_ACQ_TIME_MICROSECONDS:
|
2020-05-27 18:26:57 +02:00
|
|
|
scaled_acq = (uint64_t)ADC_ACQ_TIME_VALUE(acquisition_time) *
|
2019-02-24 15:47:57 +01:00
|
|
|
1000000U;
|
|
|
|
break;
|
|
|
|
case ADC_ACQ_TIME_NANOSECONDS:
|
2020-05-27 18:26:57 +02:00
|
|
|
scaled_acq = (uint64_t)ADC_ACQ_TIME_VALUE(acquisition_time) *
|
2019-02-24 15:47:57 +01:00
|
|
|
1000U;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sample_time = (sample_length+1) * (clk_adc / 2)
|
|
|
|
* sample_length = sample_time * (2/clk_adc) - 1,
|
|
|
|
*/
|
|
|
|
|
|
|
|
scaled_acq *= 2U;
|
|
|
|
scaled_acq += cfg->freq / 2U;
|
|
|
|
scaled_acq /= cfg->freq;
|
|
|
|
if (scaled_acq <= 1U) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
scaled_acq -= 1U;
|
|
|
|
if (scaled_acq >= 64U) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (int)scaled_acq;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int adc_sam0_channel_setup(const struct device *dev,
|
2019-02-24 15:47:57 +01:00
|
|
|
const struct adc_channel_cfg *channel_cfg)
|
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config;
|
2019-02-24 15:47:57 +01:00
|
|
|
Adc *const adc = cfg->regs;
|
|
|
|
int retval;
|
2022-01-15 23:26:19 +01:00
|
|
|
uint8_t sampctrl = 0;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
|
|
|
|
retval = adc_sam0_acquisition_to_clocks(dev,
|
|
|
|
channel_cfg->acquisition_time);
|
|
|
|
if (retval < 0) {
|
|
|
|
LOG_ERR("Selected ADC acquisition time is not valid");
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2022-01-15 23:26:19 +01:00
|
|
|
sampctrl |= ADC_SAMPCTRL_SAMPLEN(retval);
|
2019-02-24 15:47:57 +01:00
|
|
|
}
|
|
|
|
|
2022-01-15 23:26:19 +01:00
|
|
|
adc->SAMPCTRL.reg = sampctrl;
|
2019-02-24 15:47:57 +01:00
|
|
|
wait_synchronization(adc);
|
|
|
|
|
2022-01-15 23:26:19 +01:00
|
|
|
uint8_t refctrl;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
switch (channel_cfg->reference) {
|
|
|
|
case ADC_REF_INTERNAL:
|
2022-01-15 23:26:19 +01:00
|
|
|
refctrl = ADC_REFCTRL_REFSEL_INTERNAL | ADC_REFCTRL_REFCOMP;
|
2019-02-24 15:47:57 +01:00
|
|
|
/* Enable the internal bandgap reference */
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_BGEN = 1;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
2022-01-05 01:08:52 +01:00
|
|
|
#ifdef ADC_REFCTRL_REFSEL_VDD_1
|
2019-02-24 15:47:57 +01:00
|
|
|
case ADC_REF_VDD_1:
|
2022-01-15 23:26:19 +01:00
|
|
|
refctrl = ADC_REFCTRL_REFSEL_VDD_1 | ADC_REFCTRL_REFCOMP;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
2022-05-16 20:27:58 +02:00
|
|
|
case ADC_REF_VDD_1_2:
|
|
|
|
refctrl = ADC_REFCTRL_REFSEL_VDD_1_2 | ADC_REFCTRL_REFCOMP;
|
|
|
|
break;
|
2019-02-24 15:47:57 +01:00
|
|
|
case ADC_REF_EXTERNAL0:
|
2022-01-15 23:26:19 +01:00
|
|
|
refctrl = ADC_REFCTRL_REFSEL_AREFA;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
2022-09-19 20:44:33 +02:00
|
|
|
#ifdef ADC_REFCTRL_REFSEL_AREFB
|
2019-02-24 15:47:57 +01:00
|
|
|
case ADC_REF_EXTERNAL1:
|
2022-01-15 23:26:19 +01:00
|
|
|
refctrl = ADC_REFCTRL_REFSEL_AREFB;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
2022-09-19 20:44:33 +02:00
|
|
|
#endif
|
2019-02-24 15:47:57 +01:00
|
|
|
default:
|
|
|
|
LOG_ERR("Selected reference is not valid");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-01-15 23:26:19 +01:00
|
|
|
if (adc->REFCTRL.reg != refctrl) {
|
2022-01-20 03:27:57 +01:00
|
|
|
#ifdef ADC_SAM0_REFERENCE_ENABLE_PROTECTED
|
|
|
|
adc->CTRLA.bit.ENABLE = 0;
|
|
|
|
wait_synchronization(adc);
|
|
|
|
#endif
|
2022-01-15 23:26:19 +01:00
|
|
|
adc->REFCTRL.reg = refctrl;
|
2019-02-24 15:47:57 +01:00
|
|
|
wait_synchronization(adc);
|
2022-01-20 03:27:57 +01:00
|
|
|
#ifdef ADC_SAM0_REFERENCE_ENABLE_PROTECTED
|
|
|
|
adc->CTRLA.bit.ENABLE = 1;
|
|
|
|
wait_synchronization(adc);
|
|
|
|
#endif
|
2019-02-24 15:47:57 +01:00
|
|
|
#ifdef ADC_SAM0_REFERENCE_GLITCH
|
2021-08-26 11:26:09 +02:00
|
|
|
struct adc_sam0_data *data = dev->data;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
data->reference_changed = 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-15 23:26:19 +01:00
|
|
|
uint32_t inputctrl = 0;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
switch (channel_cfg->gain) {
|
|
|
|
case ADC_GAIN_1:
|
|
|
|
#ifdef ADC_INPUTCTRL_GAIN_1X
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl = ADC_INPUTCTRL_GAIN_1X;
|
2019-02-24 15:47:57 +01:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
#ifdef ADC_INPUTCTRL_GAIN_DIV2
|
|
|
|
case ADC_GAIN_1_2:
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl = ADC_INPUTCTRL_GAIN_DIV2;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef ADC_INPUTCTRL_GAIN_2X
|
|
|
|
case ADC_GAIN_2:
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl = ADC_INPUTCTRL_GAIN_2X;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef ADC_INPUTCTRL_GAIN_4X
|
|
|
|
case ADC_GAIN_4:
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl = ADC_INPUTCTRL_GAIN_4X;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef ADC_INPUTCTRL_GAIN_8X
|
|
|
|
case ADC_GAIN_8:
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl = ADC_INPUTCTRL_GAIN_8X;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef ADC_INPUTCTRL_GAIN_16X
|
|
|
|
case ADC_GAIN_16:
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl = ADC_INPUTCTRL_GAIN_16X;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
LOG_ERR("Selected ADC gain is not valid");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl |= ADC_INPUTCTRL_MUXPOS(channel_cfg->input_positive);
|
2019-02-24 15:47:57 +01:00
|
|
|
if (channel_cfg->differential) {
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl |= ADC_INPUTCTRL_MUXNEG(channel_cfg->input_negative);
|
2019-02-24 15:47:57 +01:00
|
|
|
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_DIFF(adc) |= ADC_DIFF_MASK;
|
2019-02-24 15:47:57 +01:00
|
|
|
} else {
|
2022-01-15 23:26:19 +01:00
|
|
|
inputctrl |= ADC_INPUTCTRL_MUXNEG_GND;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_DIFF(adc) &= ~ADC_DIFF_MASK;
|
2019-02-24 15:47:57 +01:00
|
|
|
}
|
2022-01-05 01:08:52 +01:00
|
|
|
wait_synchronization(adc);
|
2019-02-24 15:47:57 +01:00
|
|
|
|
2022-01-15 23:26:19 +01:00
|
|
|
adc->INPUTCTRL.reg = inputctrl;
|
2019-02-24 15:47:57 +01:00
|
|
|
wait_synchronization(adc);
|
|
|
|
|
|
|
|
/* Enable references if they're selected */
|
|
|
|
switch (channel_cfg->input_positive) {
|
|
|
|
#ifdef ADC_INPUTCTRL_MUXPOS_TEMP_Val
|
|
|
|
case ADC_INPUTCTRL_MUXPOS_TEMP_Val:
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_TSEN = 1;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef ADC_INPUTCTRL_MUXPOS_PTAT_Val
|
|
|
|
case ADC_INPUTCTRL_MUXPOS_PTAT_Val:
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_TSEN = 1;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef ADC_INPUTCTRL_MUXPOS_CTAT_Val
|
|
|
|
case ADC_INPUTCTRL_MUXPOS_CTAT_Val:
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_TSEN = 1;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case ADC_INPUTCTRL_MUXPOS_BANDGAP_Val:
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_BGEN = 1;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void adc_sam0_start_conversion(const struct device *dev)
|
2019-02-24 15:47:57 +01:00
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config;
|
2019-02-24 15:47:57 +01:00
|
|
|
Adc *const adc = cfg->regs;
|
|
|
|
|
|
|
|
LOG_DBG("Starting conversion");
|
|
|
|
|
|
|
|
adc->SWTRIG.reg = ADC_SWTRIG_START;
|
|
|
|
/*
|
|
|
|
* Should be safe to not synchronize here because the only things
|
|
|
|
* that might access the ADC after this will wait for it to complete
|
|
|
|
* (synchronize finished implicitly)
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
static void adc_context_start_sampling(struct adc_context *ctx)
|
|
|
|
{
|
|
|
|
struct adc_sam0_data *data =
|
|
|
|
CONTAINER_OF(ctx, struct adc_sam0_data, ctx);
|
|
|
|
|
|
|
|
adc_sam0_start_conversion(data->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void adc_context_update_buffer_pointer(struct adc_context *ctx,
|
|
|
|
bool repeat_sampling)
|
|
|
|
{
|
|
|
|
struct adc_sam0_data *data =
|
|
|
|
CONTAINER_OF(ctx, struct adc_sam0_data, ctx);
|
|
|
|
|
|
|
|
if (repeat_sampling) {
|
|
|
|
data->buffer = data->repeat_buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int check_buffer_size(const struct adc_sequence *sequence,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t active_channels)
|
2019-02-24 15:47:57 +01:00
|
|
|
{
|
|
|
|
size_t needed_buffer_size;
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
needed_buffer_size = active_channels * sizeof(uint16_t);
|
2019-02-24 15:47:57 +01:00
|
|
|
if (sequence->options) {
|
|
|
|
needed_buffer_size *= (1U + sequence->options->extra_samplings);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sequence->buffer_size < needed_buffer_size) {
|
|
|
|
LOG_ERR("Provided buffer is too small (%u/%u)",
|
|
|
|
sequence->buffer_size, needed_buffer_size);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int start_read(const struct device *dev,
|
|
|
|
const struct adc_sequence *sequence)
|
2019-02-24 15:47:57 +01:00
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config;
|
|
|
|
struct adc_sam0_data *data = dev->data;
|
2019-02-24 15:47:57 +01:00
|
|
|
Adc *const adc = cfg->regs;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (sequence->oversampling > 10U) {
|
|
|
|
LOG_ERR("Invalid oversampling");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
adc->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM(sequence->oversampling);
|
|
|
|
/* AVGCTRL is not synchronized */
|
|
|
|
|
|
|
|
#ifdef CONFIG_SOC_SERIES_SAMD20
|
|
|
|
/*
|
|
|
|
* Errata: silicon revisions B and C do not perform the automatic right
|
|
|
|
* shifts in accumulation
|
|
|
|
*/
|
|
|
|
if (sequence->oversampling > 4U && DSU->DID.bit.REVISION < 3) {
|
|
|
|
adc->AVGCTRL.bit.ADJRES = sequence->oversampling - 4U;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (sequence->resolution) {
|
|
|
|
case 8:
|
|
|
|
if (sequence->oversampling) {
|
|
|
|
LOG_ERR("Oversampling requires 12 bit resolution");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_RESSEL(adc) = ADC_RESSEL_8BIT;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if (sequence->oversampling) {
|
|
|
|
LOG_ERR("Oversampling requires 12 bit resolution");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_RESSEL(adc) = ADC_RESSEL_10BIT;
|
2019-02-24 15:47:57 +01:00
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
if (sequence->oversampling) {
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_RESSEL(adc) = ADC_RESSEL_16BIT;
|
2019-02-24 15:47:57 +01:00
|
|
|
} else {
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_RESSEL(adc) = ADC_RESSEL_12BIT;
|
2019-02-24 15:47:57 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_ERR("ADC resolution value %d is not valid",
|
|
|
|
sequence->resolution);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
wait_synchronization(adc);
|
|
|
|
|
2021-12-17 14:35:26 +01:00
|
|
|
if ((sequence->channels == 0)
|
|
|
|
|| ((sequence->channels & (sequence->channels - 1)) != 0)) {
|
|
|
|
/* The caller is expected to identify a single input channel, which will
|
|
|
|
* typically be the positive input, though no check is made for this...
|
|
|
|
*
|
|
|
|
* While ensuring that the channels bitfield matches the positive input
|
|
|
|
* might be sensible, this will likely break users before this revision
|
|
|
|
* was put in place.
|
|
|
|
*/
|
2019-02-24 15:47:57 +01:00
|
|
|
LOG_ERR("Channel scanning is not supported");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = check_buffer_size(sequence, 1);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->buffer = sequence->buffer;
|
|
|
|
data->repeat_buffer = sequence->buffer;
|
|
|
|
|
|
|
|
/* At this point we allow the scheduler to do other things while
|
|
|
|
* we wait for the conversions to complete. This is provided by the
|
|
|
|
* adc_context functions. However, the caller of this function is
|
|
|
|
* blocked until the results are in.
|
|
|
|
*/
|
|
|
|
adc_context_start_read(&data->ctx, sequence);
|
|
|
|
|
|
|
|
error = adc_context_wait_for_completion(&data->ctx);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int adc_sam0_read(const struct device *dev,
|
2019-02-24 15:47:57 +01:00
|
|
|
const struct adc_sequence *sequence)
|
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
struct adc_sam0_data *data = dev->data;
|
2019-02-24 15:47:57 +01:00
|
|
|
int error;
|
|
|
|
|
|
|
|
adc_context_lock(&data->ctx, false, NULL);
|
|
|
|
error = start_read(dev, sequence);
|
|
|
|
adc_context_release(&data->ctx, error);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
isr: Normalize usage of device instance through ISR
The goal of this patch is to replace the 'void *' parameter by 'struct
device *' if they use such variable or just 'const void *' on all
relevant ISRs
This will avoid not-so-nice const qualifier tweaks when device instances
will be constant.
Note that only the ISR passed to IRQ_CONNECT are of interest here.
In order to do so, the script fix_isr.py below is necessary:
from pathlib import Path
import subprocess
import pickle
import mmap
import sys
import re
import os
cocci_template = """
@r_fix_isr_0
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
...
(
const struct device *D = (const struct device *)P;
|
const struct device *D = P;
)
...
}
@r_fix_isr_1
@
type ret_type;
identifier P;
identifier D;
@@
-ret_type <!fn!>(void *P)
+ret_type <!fn!>(const struct device *P)
{
...
const struct device *D;
...
(
D = (const struct device *)P;
|
D = P;
)
...
}
@r_fix_isr_2
@
type ret_type;
identifier A;
@@
-ret_type <!fn!>(void *A)
+ret_type <!fn!>(const void *A)
{
...
}
@r_fix_isr_3
@
const struct device *D;
@@
-<!fn!>((void *)D);
+<!fn!>(D);
@r_fix_isr_4
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
...
(
-const struct device *D = (const struct device *)P;
|
-const struct device *D = P;
)
...
}
@r_fix_isr_5
@
type ret_type;
identifier D;
identifier P;
@@
-ret_type <!fn!>(const struct device *P)
+ret_type <!fn!>(const struct device *D)
{
...
-const struct device *D;
...
(
-D = (const struct device *)P;
|
-D = P;
)
...
}
"""
def find_isr(fn):
db = []
data = None
start = 0
try:
with open(fn, 'r+') as f:
data = str(mmap.mmap(f.fileno(), 0).read())
except Exception as e:
return db
while True:
isr = ""
irq = data.find('IRQ_CONNECT', start)
while irq > -1:
p = 1
arg = 1
p_o = data.find('(', irq)
if p_o < 0:
irq = -1
break;
pos = p_o + 1
while p > 0:
if data[pos] == ')':
p -= 1
elif data[pos] == '(':
p += 1
elif data[pos] == ',' and p == 1:
arg += 1
if arg == 3:
isr += data[pos]
pos += 1
isr = isr.strip(',\\n\\t ')
if isr not in db and len(isr) > 0:
db.append(isr)
start = pos
break
if irq < 0:
break
return db
def patch_isr(fn, isr_list):
if len(isr_list) <= 0:
return
for isr in isr_list:
tmplt = cocci_template.replace('<!fn!>', isr)
with open('/tmp/isr_fix.cocci', 'w') as f:
f.write(tmplt)
cmd = ['spatch', '--sp-file', '/tmp/isr_fix.cocci', '--in-place', fn]
subprocess.run(cmd)
def process_files(path):
if path.is_file() and path.suffix in ['.h', '.c']:
p = str(path.parent) + '/' + path.name
isr_list = find_isr(p)
patch_isr(p, isr_list)
elif path.is_dir():
for p in path.iterdir():
process_files(p)
if len(sys.argv) < 2:
print("You need to provide a dir/file path")
sys.exit(1)
process_files(Path(sys.argv[1]))
And is run: ./fix_isr.py <zephyr root directory>
Finally, some files needed manual fixes such.
Fixes #27399
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2020-06-17 14:58:56 +02:00
|
|
|
static void adc_sam0_isr(const struct device *dev)
|
2019-02-24 15:47:57 +01:00
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
struct adc_sam0_data *data = dev->data;
|
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config;
|
2019-02-24 15:47:57 +01:00
|
|
|
Adc *const adc = cfg->regs;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t result;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
adc->INTFLAG.reg = ADC_INTFLAG_MASK;
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
result = (uint16_t)(adc->RESULT.reg);
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#ifdef ADC_SAM0_REFERENCE_GLITCH
|
|
|
|
if (data->reference_changed) {
|
|
|
|
data->reference_changed = 0;
|
|
|
|
LOG_DBG("Discarded initial conversion due to reference change");
|
|
|
|
|
|
|
|
adc_sam0_start_conversion(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
*data->buffer++ = result;
|
|
|
|
adc_context_on_sampling_done(&data->ctx, dev);
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int adc_sam0_init(const struct device *dev)
|
2019-02-24 15:47:57 +01:00
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config;
|
|
|
|
struct adc_sam0_data *data = dev->data;
|
2019-02-24 15:47:57 +01:00
|
|
|
Adc *const adc = cfg->regs;
|
2022-03-13 17:17:01 +01:00
|
|
|
int retval;
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#ifdef MCLK
|
|
|
|
GCLK->PCHCTRL[cfg->gclk_id].reg = cfg->gclk_mask | GCLK_PCHCTRL_CHEN;
|
|
|
|
|
2022-09-19 20:44:33 +02:00
|
|
|
MCLK_ADC |= cfg->mclk_mask;
|
2019-02-24 15:47:57 +01:00
|
|
|
#else
|
|
|
|
PM->APBCMASK.bit.ADC_ = 1;
|
|
|
|
|
|
|
|
GCLK->CLKCTRL.reg = cfg->gclk | GCLK_CLKCTRL_CLKEN;
|
|
|
|
#endif
|
|
|
|
|
2022-03-13 17:17:01 +01:00
|
|
|
retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
|
|
|
|
if (retval < 0) {
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2022-01-05 01:08:52 +01:00
|
|
|
ADC_PRESCALER(adc) = cfg->prescaler;
|
2019-02-24 15:47:57 +01:00
|
|
|
wait_synchronization(adc);
|
|
|
|
|
|
|
|
adc->INTENCLR.reg = ADC_INTENCLR_MASK;
|
|
|
|
adc->INTFLAG.reg = ADC_INTFLAG_MASK;
|
|
|
|
|
|
|
|
cfg->config_func(dev);
|
|
|
|
|
|
|
|
adc->INTENSET.reg = ADC_INTENSET_RESRDY;
|
|
|
|
|
|
|
|
data->dev = dev;
|
|
|
|
#ifdef ADC_SAM0_REFERENCE_GLITCH
|
|
|
|
data->reference_changed = 1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
adc->CTRLA.bit.ENABLE = 1;
|
|
|
|
wait_synchronization(adc);
|
|
|
|
|
|
|
|
adc_context_unlock_unconditionally(&data->ctx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_ADC_ASYNC
|
2020-04-30 20:33:38 +02:00
|
|
|
static int adc_sam0_read_async(const struct device *dev,
|
2019-02-24 15:47:57 +01:00
|
|
|
const struct adc_sequence *sequence,
|
|
|
|
struct k_poll_signal *async)
|
|
|
|
{
|
2021-08-26 11:26:09 +02:00
|
|
|
struct adc_sam0_data *data = dev->data;
|
2019-02-24 15:47:57 +01:00
|
|
|
int error;
|
|
|
|
|
|
|
|
adc_context_lock(&data->ctx, true, async);
|
|
|
|
error = start_read(dev, sequence);
|
|
|
|
adc_context_release(&data->ctx, error);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const struct adc_driver_api adc_sam0_api = {
|
|
|
|
.channel_setup = adc_sam0_channel_setup,
|
|
|
|
.read = adc_sam0_read,
|
|
|
|
#ifdef CONFIG_ADC_ASYNC
|
|
|
|
.read_async = adc_sam0_read_async,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MCLK
|
|
|
|
|
2020-04-28 23:35:58 +02:00
|
|
|
#define ADC_SAM0_CLOCK_CONTROL(n) \
|
|
|
|
.mclk_mask = BIT(DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, bit)), \
|
|
|
|
.gclk_mask = UTIL_CAT(GCLK_PCHCTRL_GEN_GCLK, \
|
|
|
|
DT_INST_PROP(n, gclk)), \
|
|
|
|
.gclk_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, periph_ch), \
|
2022-01-05 01:08:52 +01:00
|
|
|
.prescaler = UTIL_CAT(ADC_CTRLx_PRESCALER_DIV, \
|
|
|
|
UTIL_CAT(DT_INST_PROP(n, prescaler), _Val)),
|
2020-04-28 23:35:58 +02:00
|
|
|
|
|
|
|
#define ADC_SAM0_CONFIGURE(n) \
|
|
|
|
do { \
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config; \
|
2020-04-28 23:35:58 +02:00
|
|
|
Adc * const adc = cfg->regs; \
|
2022-01-05 01:08:52 +01:00
|
|
|
adc->CALIB.reg = ADC_SAM0_BIASCOMP(n) \
|
|
|
|
| ADC_SAM0_BIASR2R(n) \
|
|
|
|
| ADC_SAM0_BIASREFBUF(n); \
|
2022-07-20 08:37:40 +02:00
|
|
|
} while (false)
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2020-04-28 23:35:58 +02:00
|
|
|
#define ADC_SAM0_CLOCK_CONTROL(n) \
|
|
|
|
.gclk = UTIL_CAT(GCLK_CLKCTRL_GEN_GCLK, DT_INST_PROP(n, gclk)) |\
|
|
|
|
GCLK_CLKCTRL_ID_ADC, \
|
2022-01-05 01:08:52 +01:00
|
|
|
.prescaler = UTIL_CAT(ADC_CTRLx_PRESCALER_DIV, \
|
|
|
|
UTIL_CAT(DT_INST_PROP(n, prescaler), _Val)),
|
2020-04-28 23:35:58 +02:00
|
|
|
|
|
|
|
#define ADC_SAM0_CONFIGURE(n) \
|
|
|
|
do { \
|
2021-08-26 11:26:09 +02:00
|
|
|
const struct adc_sam0_cfg *const cfg = dev->config; \
|
2020-04-28 23:35:58 +02:00
|
|
|
Adc * const adc = cfg->regs; \
|
|
|
|
/* Linearity is split across two words */ \
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t lin = ((*(uint32_t *)ADC_FUSES_LINEARITY_0_ADDR) & \
|
2020-04-28 23:35:58 +02:00
|
|
|
ADC_FUSES_LINEARITY_0_Msk) >> \
|
|
|
|
ADC_FUSES_LINEARITY_0_Pos; \
|
2020-05-27 18:26:57 +02:00
|
|
|
lin |= (((*(uint32_t *)ADC_FUSES_LINEARITY_1_ADDR) & \
|
2020-04-28 23:35:58 +02:00
|
|
|
ADC_FUSES_LINEARITY_1_Msk) >> \
|
|
|
|
ADC_FUSES_LINEARITY_1_Pos) << 4; \
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t bias = ((*(uint32_t *)ADC_FUSES_BIASCAL_ADDR) & \
|
2020-04-28 23:35:58 +02:00
|
|
|
ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos; \
|
|
|
|
adc->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | \
|
|
|
|
ADC_CALIB_LINEARITY_CAL(lin); \
|
2022-07-20 08:37:40 +02:00
|
|
|
} while (false)
|
2019-02-24 15:47:57 +01:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-04-28 23:35:58 +02:00
|
|
|
#define ADC_SAM0_DEVICE(n) \
|
2022-03-13 17:17:01 +01:00
|
|
|
PINCTRL_DT_INST_DEFINE(n); \
|
2020-07-14 17:02:00 +02:00
|
|
|
static void adc_sam0_config_##n(const struct device *dev); \
|
2020-04-28 23:35:58 +02:00
|
|
|
static const struct adc_sam0_cfg adc_sam_cfg_##n = { \
|
|
|
|
.regs = (Adc *)DT_INST_REG_ADDR(n), \
|
|
|
|
ADC_SAM0_CLOCK_CONTROL(n) \
|
|
|
|
.freq = UTIL_CAT(UTIL_CAT(SOC_ATMEL_SAM0_GCLK, \
|
|
|
|
DT_INST_PROP(n, gclk)), \
|
|
|
|
_FREQ_HZ) / \
|
|
|
|
DT_INST_PROP(n, prescaler), \
|
|
|
|
.config_func = &adc_sam0_config_##n, \
|
2022-03-13 17:17:01 +01:00
|
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
2020-04-28 23:35:58 +02:00
|
|
|
}; \
|
|
|
|
static struct adc_sam0_data adc_sam_data_##n = { \
|
|
|
|
ADC_CONTEXT_INIT_TIMER(adc_sam_data_##n, ctx), \
|
|
|
|
ADC_CONTEXT_INIT_LOCK(adc_sam_data_##n, ctx), \
|
|
|
|
ADC_CONTEXT_INIT_SYNC(adc_sam_data_##n, ctx), \
|
|
|
|
}; \
|
2021-04-28 10:02:28 +02:00
|
|
|
DEVICE_DT_INST_DEFINE(n, adc_sam0_init, NULL, \
|
2020-12-09 19:49:32 +01:00
|
|
|
&adc_sam_data_##n, \
|
2020-04-28 23:35:58 +02:00
|
|
|
&adc_sam_cfg_##n, POST_KERNEL, \
|
2021-10-19 21:45:49 +02:00
|
|
|
CONFIG_ADC_INIT_PRIORITY, \
|
2020-04-28 23:35:58 +02:00
|
|
|
&adc_sam0_api); \
|
2020-07-14 17:02:00 +02:00
|
|
|
static void adc_sam0_config_##n(const struct device *dev) \
|
2020-04-28 23:35:58 +02:00
|
|
|
{ \
|
2021-09-01 03:07:31 +02:00
|
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, resrdy, irq), \
|
|
|
|
DT_INST_IRQ_BY_NAME(n, resrdy, priority), \
|
2020-04-28 23:35:58 +02:00
|
|
|
adc_sam0_isr, \
|
2020-12-09 19:49:32 +01:00
|
|
|
DEVICE_DT_INST_GET(n), 0); \
|
2021-09-01 03:07:31 +02:00
|
|
|
irq_enable(DT_INST_IRQ_BY_NAME(n, resrdy, irq)); \
|
2020-04-28 23:35:58 +02:00
|
|
|
ADC_SAM0_CONFIGURE(n); \
|
2019-02-24 15:47:57 +01:00
|
|
|
}
|
|
|
|
|
2020-05-06 20:23:07 +02:00
|
|
|
DT_INST_FOREACH_STATUS_OKAY(ADC_SAM0_DEVICE)
|