drivers: adc: Add SAM0 ADC driver
This adds a driver for the SAM0 series ADC. Tested with /tests/drivers/adc/adc_api on SAMD21. Signed-off-by: Derek Hageman <hageman@inthat.cloud>
This commit is contained in:
parent
9a6bbbfb69
commit
1d64a46501
9 changed files with 703 additions and 0 deletions
615
drivers/adc/adc_sam0.c
Normal file
615
drivers/adc/adc_sam0.c
Normal file
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Derek Hageman <hageman@inthat.cloud>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#include <soc.h>
|
||||
#include <adc.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
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;
|
||||
struct device *dev;
|
||||
|
||||
u16_t *buffer;
|
||||
|
||||
/*
|
||||
* Saved initial start, so we can reset the advances we've done
|
||||
* if required
|
||||
*/
|
||||
u16_t *repeat_buffer;
|
||||
|
||||
#ifdef ADC_SAM0_REFERENCE_GLITCH
|
||||
u8_t reference_changed;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct adc_sam0_cfg {
|
||||
Adc *regs;
|
||||
|
||||
#ifdef MCLK
|
||||
u32_t mclk_mask;
|
||||
u32_t gclk_mask;
|
||||
u16_t gclk_id;
|
||||
#else
|
||||
u32_t gclk;
|
||||
#endif
|
||||
|
||||
u32_t freq;
|
||||
u16_t prescaler;
|
||||
|
||||
void (*config_func)(struct device *dev);
|
||||
};
|
||||
|
||||
#define DEV_CFG(dev) \
|
||||
((const struct adc_sam0_cfg *const)(dev)->config->config_info)
|
||||
#define DEV_DATA(dev) \
|
||||
((struct adc_sam0_data *)(dev)->driver_data)
|
||||
|
||||
static void wait_synchronization(Adc *const adc)
|
||||
{
|
||||
#if defined(ADC_SYNCBUSY_MASK)
|
||||
while ((adc->SYNCBUSY.reg & ADC_SYNCBUSY_MASK) != 0) {
|
||||
}
|
||||
#else
|
||||
while ((adc->STATUS.reg & ADC_STATUS_SYNCBUSY) != 0) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int adc_sam0_acquisition_to_clocks(struct device *dev,
|
||||
u16_t acquisition_time)
|
||||
{
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev);
|
||||
u64_t scaled_acq;
|
||||
|
||||
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:
|
||||
scaled_acq = (u64_t)ADC_ACQ_TIME_VALUE(acquisition_time) *
|
||||
1000000U;
|
||||
break;
|
||||
case ADC_ACQ_TIME_NANOSECONDS:
|
||||
scaled_acq = (u64_t)ADC_ACQ_TIME_VALUE(acquisition_time) *
|
||||
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;
|
||||
}
|
||||
|
||||
static int adc_sam0_channel_setup(struct device *dev,
|
||||
const struct adc_channel_cfg *channel_cfg)
|
||||
{
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev);
|
||||
Adc *const adc = cfg->regs;
|
||||
int retval;
|
||||
u8_t SAMPCTRL = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SAMPCTRL |= ADC_SAMPCTRL_SAMPLEN(retval);
|
||||
}
|
||||
|
||||
adc->SAMPCTRL.reg = SAMPCTRL;
|
||||
wait_synchronization(adc);
|
||||
|
||||
|
||||
u8_t REFCTRL;
|
||||
|
||||
switch (channel_cfg->reference) {
|
||||
case ADC_REF_INTERNAL:
|
||||
#ifdef ADC_REFCTRL_REFSEL_INTREF
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_INTREF | ADC_REFCTRL_REFCOMP;
|
||||
/* Enable the internal reference, defaulting to 1V */
|
||||
SUPC->VREF.bit.VREFOE = 1;
|
||||
#else
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_INT1V | ADC_REFCTRL_REFCOMP;
|
||||
/* Enable the internal bandgap reference */
|
||||
SYSCTRL->VREF.bit.BGOUTEN = 1;
|
||||
#endif
|
||||
break;
|
||||
case ADC_REF_VDD_1_2:
|
||||
#ifdef ADC_REFCTRL_REFSEL_INTVCC0
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_INTVCC0 | ADC_REFCTRL_REFCOMP;
|
||||
#else
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_INTVCC1 | ADC_REFCTRL_REFCOMP;
|
||||
#endif
|
||||
break;
|
||||
#ifdef ADC_REFCTRL_REFSEL_INTVCC1
|
||||
case ADC_REF_VDD_1:
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_INTVCC1 | ADC_REFCTRL_REFCOMP;
|
||||
break;
|
||||
#endif
|
||||
case ADC_REF_EXTERNAL0:
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_AREFA;
|
||||
break;
|
||||
case ADC_REF_EXTERNAL1:
|
||||
REFCTRL = ADC_REFCTRL_REFSEL_AREFB;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Selected reference is not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (adc->REFCTRL.reg != REFCTRL) {
|
||||
adc->REFCTRL.reg = REFCTRL;
|
||||
wait_synchronization(adc);
|
||||
#ifdef ADC_SAM0_REFERENCE_GLITCH
|
||||
struct adc_sam0_data *data = DEV_DATA(dev);
|
||||
|
||||
data->reference_changed = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
u32_t INPUTCTRL = 0;
|
||||
|
||||
switch (channel_cfg->gain) {
|
||||
case ADC_GAIN_1:
|
||||
#ifdef ADC_INPUTCTRL_GAIN_1X
|
||||
INPUTCTRL = ADC_INPUTCTRL_GAIN_1X;
|
||||
#endif
|
||||
break;
|
||||
#ifdef ADC_INPUTCTRL_GAIN_DIV2
|
||||
case ADC_GAIN_1_2:
|
||||
INPUTCTRL = ADC_INPUTCTRL_GAIN_DIV2;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ADC_INPUTCTRL_GAIN_2X
|
||||
case ADC_GAIN_2:
|
||||
INPUTCTRL = ADC_INPUTCTRL_GAIN_2X;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ADC_INPUTCTRL_GAIN_4X
|
||||
case ADC_GAIN_4:
|
||||
INPUTCTRL = ADC_INPUTCTRL_GAIN_4X;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ADC_INPUTCTRL_GAIN_8X
|
||||
case ADC_GAIN_8:
|
||||
INPUTCTRL = ADC_INPUTCTRL_GAIN_8X;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ADC_INPUTCTRL_GAIN_16X
|
||||
case ADC_GAIN_16:
|
||||
INPUTCTRL = ADC_INPUTCTRL_GAIN_16X;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
LOG_ERR("Selected ADC gain is not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
INPUTCTRL |= ADC_INPUTCTRL_MUXPOS(channel_cfg->input_positive);
|
||||
if (channel_cfg->differential) {
|
||||
INPUTCTRL |= ADC_INPUTCTRL_MUXNEG(channel_cfg->input_negative);
|
||||
|
||||
#ifdef ADC_INPUTCTRL_DIFFMODE
|
||||
INPUTCTRL |= ADC_INPUTCTRL_DIFFMODE;
|
||||
#else
|
||||
adc->CTRLB.bit.DIFFMODE = 1;
|
||||
wait_synchronization(adc);
|
||||
#endif
|
||||
} else {
|
||||
INPUTCTRL |= ADC_INPUTCTRL_MUXNEG_GND;
|
||||
|
||||
#ifndef ADC_INPUTCTRL_DIFFMODE
|
||||
adc->CTRLB.bit.DIFFMODE = 0;
|
||||
wait_synchronization(adc);
|
||||
#endif
|
||||
}
|
||||
|
||||
adc->INPUTCTRL.reg = INPUTCTRL;
|
||||
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:
|
||||
SYSCTRL->VREF.bit.TSEN = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ADC_INPUTCTRL_MUXPOS_PTAT_Val
|
||||
case ADC_INPUTCTRL_MUXPOS_PTAT_Val:
|
||||
SUPC->VREF.bit.TSEN = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ADC_INPUTCTRL_MUXPOS_CTAT_Val
|
||||
case ADC_INPUTCTRL_MUXPOS_CTAT_Val:
|
||||
SUPC->VREF.bit.TSEN = 1;
|
||||
break;
|
||||
#endif
|
||||
case ADC_INPUTCTRL_MUXPOS_BANDGAP_Val:
|
||||
#ifdef ADC_REFCTRL_REFSEL_INTREF
|
||||
SUPC->VREF.bit.VREFOE = 1;
|
||||
#else
|
||||
SYSCTRL->VREF.bit.BGOUTEN = 1;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adc_sam0_start_conversion(struct device *dev)
|
||||
{
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev);
|
||||
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,
|
||||
u8_t active_channels)
|
||||
{
|
||||
size_t needed_buffer_size;
|
||||
|
||||
needed_buffer_size = active_channels * sizeof(u16_t);
|
||||
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;
|
||||
}
|
||||
|
||||
static int start_read(struct device *dev, const struct adc_sequence *sequence)
|
||||
{
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev);
|
||||
struct adc_sam0_data *data = DEV_DATA(dev);
|
||||
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;
|
||||
}
|
||||
|
||||
adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val;
|
||||
break;
|
||||
case 10:
|
||||
if (sequence->oversampling) {
|
||||
LOG_ERR("Oversampling requires 12 bit resolution");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
|
||||
break;
|
||||
case 12:
|
||||
if (sequence->oversampling) {
|
||||
adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val;
|
||||
} else {
|
||||
adc->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("ADC resolution value %d is not valid",
|
||||
sequence->resolution);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wait_synchronization(adc);
|
||||
|
||||
if (sequence->channels != 1U) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int adc_sam0_read(struct device *dev,
|
||||
const struct adc_sequence *sequence)
|
||||
{
|
||||
struct adc_sam0_data *data = DEV_DATA(dev);
|
||||
int error;
|
||||
|
||||
adc_context_lock(&data->ctx, false, NULL);
|
||||
error = start_read(dev, sequence);
|
||||
adc_context_release(&data->ctx, error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void adc_sam0_isr(void *arg)
|
||||
{
|
||||
struct device *dev = (struct device *)arg;
|
||||
struct adc_sam0_data *data = DEV_DATA(dev);
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev);
|
||||
Adc *const adc = cfg->regs;
|
||||
u16_t result;
|
||||
|
||||
adc->INTFLAG.reg = ADC_INTFLAG_MASK;
|
||||
|
||||
result = (u16_t)(adc->RESULT.reg);
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
static int adc_sam0_init(struct device *dev)
|
||||
{
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev);
|
||||
struct adc_sam0_data *data = DEV_DATA(dev);
|
||||
Adc *const adc = cfg->regs;
|
||||
|
||||
#ifdef MCLK
|
||||
GCLK->PCHCTRL[cfg->gclk_id].reg = cfg->gclk_mask | GCLK_PCHCTRL_CHEN;
|
||||
|
||||
MCLK->APBDMASK.reg |= cfg->mclk_mask;
|
||||
#else
|
||||
PM->APBCMASK.bit.ADC_ = 1;
|
||||
|
||||
GCLK->CLKCTRL.reg = cfg->gclk | GCLK_CLKCTRL_CLKEN;
|
||||
#endif
|
||||
|
||||
#ifdef ADC_CTRLA_PRESCALER_Pos
|
||||
adc->CTRLA.reg = cfg->prescaler;
|
||||
#else
|
||||
adc->CTRLB.reg = cfg->prescaler;
|
||||
#endif
|
||||
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
|
||||
static int adc_sam0_read_async(struct device *dev,
|
||||
const struct adc_sequence *sequence,
|
||||
struct k_poll_signal *async)
|
||||
{
|
||||
struct adc_sam0_data *data = DEV_DATA(dev);
|
||||
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
|
||||
|
||||
#define ADC_SAM0_CLOCK_CONTROL(n) \
|
||||
.mclk_mask = MCLK_APBDMASK_ADC##n, \
|
||||
.gclk_mask = UTIL_CAT(GCLK_PCHCTRL_GEN_GCLK, \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_GCLK), \
|
||||
.gclk_id = ADC##n##_GCLK_ID, \
|
||||
.prescaler = UTIL_CAT(ADC_CTRLA_PRESCALER_DIV, \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_PRESCALER),
|
||||
#define ADC_SAM0_CONFIGURE(n) do { \
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev); \
|
||||
Adc *const adc = cfg->regs; \
|
||||
u32_t comp = ((*(u32_t *)ADC##n##_FUSES_BIASCOMP_ADDR) & \
|
||||
ADC##n##_FUSES_BIASCOMP_Msk) >> \
|
||||
ADC##n##_FUSES_BIASCOMP_Pos; \
|
||||
u32_t r2r = ((*(u32_t *)ADC##n##_FUSES_BIASR2R_ADDR) & \
|
||||
ADC##n##_FUSES_BIASR2R_Msk) >> \
|
||||
ADC##n##_FUSES_BIASR2R_Pos; \
|
||||
u32_t rbuf = ((*(u32_t *)ADC##n##_FUSES_BIASREFBUF_ADDR) & \
|
||||
ADC##n##_FUSES_BIASREFBUF_Msk) >> \
|
||||
ADC##n##_FUSES_BIASREFBUF_Pos; \
|
||||
adc->CALIB.reg = ADC_CALIB_BIASCOMP(comp) | \
|
||||
ADC_CALIB_BIASR2R(r2r) | \
|
||||
ADC_CALIB_BIASREFBUF(rbuf); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define ADC_SAM0_CLOCK_CONTROL(n) \
|
||||
.gclk = UTIL_CAT(GCLK_CLKCTRL_GEN_GCLK, \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_GCLK) | \
|
||||
GCLK_CLKCTRL_ID_ADC, \
|
||||
.prescaler = UTIL_CAT(ADC_CTRLB_PRESCALER_DIV, \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_PRESCALER),
|
||||
#define ADC_SAM0_CONFIGURE(n) do { \
|
||||
const struct adc_sam0_cfg *const cfg = DEV_CFG(dev); \
|
||||
Adc *const adc = cfg->regs; \
|
||||
/* Linearity is split across two words */ \
|
||||
u32_t lin = ((*(u32_t *)ADC_FUSES_LINEARITY_0_ADDR) & \
|
||||
ADC_FUSES_LINEARITY_0_Msk) >> \
|
||||
ADC_FUSES_LINEARITY_0_Pos; \
|
||||
lin |= (((*(u32_t *)ADC_FUSES_LINEARITY_1_ADDR) & \
|
||||
ADC_FUSES_LINEARITY_1_Msk) >> \
|
||||
ADC_FUSES_LINEARITY_1_Pos) \
|
||||
<< 4; \
|
||||
u32_t bias = ((*(u32_t *)ADC_FUSES_BIASCAL_ADDR) & \
|
||||
ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;\
|
||||
adc->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | \
|
||||
ADC_CALIB_LINEARITY_CAL(lin); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define ADC_SAM0_DEVICE(n) \
|
||||
static void adc_sam0_config_##n(struct device *dev); \
|
||||
static const struct adc_sam0_cfg adc_sam_cfg_##n = { \
|
||||
.regs = (Adc *)DT_ATMEL_SAM0_ADC_ADC_##n##_BASE_ADDRESS, \
|
||||
ADC_SAM0_CLOCK_CONTROL(n) \
|
||||
.freq = UTIL_CAT(UTIL_CAT(SOC_ATMEL_SAM0_GCLK, \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_GCLK), \
|
||||
_FREQ_HZ) / \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_PRESCALER, \
|
||||
.config_func = &adc_sam0_config_##n, \
|
||||
}; \
|
||||
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), \
|
||||
}; \
|
||||
DEVICE_AND_API_INIT(adc0_sam_##n, DT_ATMEL_SAM0_ADC_ADC_##n##_LABEL, \
|
||||
adc_sam0_init, &adc_sam_data_##n, &adc_sam_cfg_##n,\
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
&adc_sam0_api); \
|
||||
static void adc_sam0_config_##n(struct device *dev) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_ATMEL_SAM0_ADC_ADC_##n##_IRQ, \
|
||||
DT_ATMEL_SAM0_ADC_ADC_##n##_IRQ_PRIORITY, \
|
||||
adc_sam0_isr, \
|
||||
DEVICE_GET(adc0_sam_##n), \
|
||||
0); \
|
||||
irq_enable(DT_ATMEL_SAM0_ADC_ADC_##n##_IRQ); \
|
||||
ADC_SAM0_CONFIGURE(n); \
|
||||
}
|
||||
|
||||
#if DT_ATMEL_SAM0_ADC_ADC_0_BASE_ADDRESS
|
||||
ADC_SAM0_DEVICE(0);
|
||||
#endif
|
||||
|
||||
#if DT_ATMEL_SAM0_ADC_ADC_1_BASE_ADDRESS
|
||||
ADC_SAM0_DEVICE(1);
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue