zephyr/drivers/adc/adc_b91.c
Pieter De Gendt 8442b6a83f drivers: adc: Place API into iterable section
Move all adc driver api structs into an iterable section, this allows us
to verify if an api pointer is located in compatible linker section.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
2024-11-29 14:50:40 +01:00

468 lines
11 KiB
C

/*
* Copyright (c) 2022 Telink Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT telink_b91_adc
/* Local driver headers */
#define ADC_CONTEXT_USES_KERNEL_TIMER
#include "adc_context.h"
/* Zephyr Device Tree headers */
#include <zephyr/dt-bindings/adc/b91-adc.h>
/* Zephyr Logging headers */
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(adc_b91, CONFIG_ADC_LOG_LEVEL);
/* Telink HAL headers */
#include <adc.h>
/* ADC B91 defines */
#define SIGN_BIT_POSITION (13)
#define AREG_ADC_DATA_STATUS (0xf6)
#define ADC_DATA_READY BIT(0)
/* B91 ADC driver data */
struct b91_adc_data {
struct adc_context ctx;
int16_t *buffer;
int16_t *repeat_buffer;
uint8_t differential;
uint8_t resolution_divider;
struct k_sem acq_sem;
struct k_thread thread;
K_KERNEL_STACK_MEMBER(stack, CONFIG_ADC_B91_ACQUISITION_THREAD_STACK_SIZE);
};
struct b91_adc_cfg {
uint32_t sample_freq;
uint16_t vref_internal_mv;
};
/* Validate ADC data buffer size */
static int adc_b91_validate_buffer_size(const struct adc_sequence *sequence)
{
size_t needed = sizeof(int16_t);
if (sequence->options) {
needed *= (1 + sequence->options->extra_samplings);
}
if (sequence->buffer_size < needed) {
return -ENOMEM;
}
return 0;
}
/* Validate ADC read API input parameters */
static int adc_b91_validate_sequence(const struct adc_sequence *sequence)
{
int status;
if (sequence->channels != BIT(0)) {
LOG_ERR("Only channel 0 is supported.");
return -ENOTSUP;
}
if (sequence->oversampling) {
LOG_ERR("Oversampling is not supported.");
return -ENOTSUP;
}
status = adc_b91_validate_buffer_size(sequence);
if (status) {
LOG_ERR("Buffer size too small.");
return status;
}
return 0;
}
/* Convert dts pin to B91 SDK pin */
static adc_input_pin_def_e adc_b91_get_pin(uint8_t dt_pin)
{
adc_input_pin_def_e adc_pin;
switch (dt_pin) {
case DT_ADC_GPIO_PB0:
adc_pin = ADC_GPIO_PB0;
break;
case DT_ADC_GPIO_PB1:
adc_pin = ADC_GPIO_PB1;
break;
case DT_ADC_GPIO_PB2:
adc_pin = ADC_GPIO_PB2;
break;
case DT_ADC_GPIO_PB3:
adc_pin = ADC_GPIO_PB3;
break;
case DT_ADC_GPIO_PB4:
adc_pin = ADC_GPIO_PB4;
break;
case DT_ADC_GPIO_PB5:
adc_pin = ADC_GPIO_PB5;
break;
case DT_ADC_GPIO_PB6:
adc_pin = ADC_GPIO_PB6;
break;
case DT_ADC_GPIO_PB7:
adc_pin = ADC_GPIO_PB7;
break;
case DT_ADC_GPIO_PD0:
adc_pin = ADC_GPIO_PD0;
break;
case DT_ADC_GPIO_PD1:
adc_pin = ADC_GPIO_PD1;
break;
case DT_ADC_VBAT:
adc_pin = ADC_VBAT;
break;
default:
adc_pin = NOINPUTN;
break;
}
return adc_pin;
}
/* Get ADC value */
static signed short adc_b91_get_code(void)
{
signed short adc_code;
analog_write_reg8(areg_adc_data_sample_control,
analog_read_reg8(areg_adc_data_sample_control) | FLD_NOT_SAMPLE_ADC_DATA);
adc_code = analog_read_reg16(areg_adc_misc_l);
analog_write_reg8(areg_adc_data_sample_control,
analog_read_reg8(areg_adc_data_sample_control) & (~FLD_NOT_SAMPLE_ADC_DATA));
return adc_code;
}
/* ADC Context API implementation: start sampling */
static void adc_context_start_sampling(struct adc_context *ctx)
{
struct b91_adc_data *data =
CONTAINER_OF(ctx, struct b91_adc_data, ctx);
data->repeat_buffer = data->buffer;
adc_power_on();
k_sem_give(&data->acq_sem);
}
/* ADC Context API implementation: buffer pointer */
static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling)
{
struct b91_adc_data *data =
CONTAINER_OF(ctx, struct b91_adc_data, ctx);
if (repeat_sampling) {
data->buffer = data->repeat_buffer;
}
}
/* Start ADC measurements */
static int adc_b91_adc_start_read(const struct device *dev, const struct adc_sequence *sequence)
{
int status;
struct b91_adc_data *data = dev->data;
/* Validate input parameters */
status = adc_b91_validate_sequence(sequence);
if (status != 0) {
return status;
}
/* Set resolution */
switch (sequence->resolution) {
case 14:
adc_set_resolution(ADC_RES14);
data->resolution_divider = 1;
break;
case 12:
adc_set_resolution(ADC_RES12);
data->resolution_divider = 4;
break;
case 10:
adc_set_resolution(ADC_RES10);
data->resolution_divider = 16;
break;
case 8:
adc_set_resolution(ADC_RES8);
data->resolution_divider = 64;
break;
default:
LOG_ERR("Selected ADC resolution is not supported.");
return -EINVAL;
}
/* Save buffer */
data->buffer = sequence->buffer;
/* Start ADC conversion */
adc_context_start_read(&data->ctx, sequence);
return adc_context_wait_for_completion(&data->ctx);
}
/* Main ADC Acquisition thread */
static void adc_b91_acquisition_thread(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p2);
ARG_UNUSED(p3);
const struct device *dev = p1;
int16_t adc_code;
struct b91_adc_data *data = dev->data;
while (true) {
/* Wait for Acquisition semaphore */
k_sem_take(&data->acq_sem, K_FOREVER);
/* Wait for ADC data ready */
while ((analog_read_reg8(AREG_ADC_DATA_STATUS) & ADC_DATA_READY)
!= ADC_DATA_READY) {
}
/* Perform read */
adc_code = (adc_b91_get_code() / data->resolution_divider);
if (!data->differential) {
/* Sign bit is not used in case of single-ended configuration */
adc_code = adc_code * 2;
/* Do not return negative value for single-ended configuration */
if (adc_code < 0) {
adc_code = 0;
}
}
*data->buffer++ = adc_code;
/* Power off ADC */
adc_power_off();
/* Release ADC context */
adc_context_on_sampling_done(&data->ctx, dev);
}
}
/* ADC Driver initialization */
static int adc_b91_init(const struct device *dev)
{
struct b91_adc_data *data = dev->data;
k_sem_init(&data->acq_sem, 0, 1);
k_thread_create(&data->thread, data->stack,
CONFIG_ADC_B91_ACQUISITION_THREAD_STACK_SIZE,
adc_b91_acquisition_thread,
(void *)dev, NULL, NULL,
CONFIG_ADC_B91_ACQUISITION_THREAD_PRIO,
0, K_NO_WAIT);
adc_context_unlock_unconditionally(&data->ctx);
return 0;
}
/* API implementation: channel_setup */
static int adc_b91_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
adc_ref_vol_e vref_internal_mv;
adc_sample_freq_e sample_freq;
adc_pre_scale_e pre_scale;
adc_sample_cycle_e sample_cycl;
adc_input_pin_def_e input_positive;
adc_input_pin_def_e input_negative;
struct b91_adc_data *data = dev->data;
const struct b91_adc_cfg *config = dev->config;
/* Check channel ID */
if (channel_cfg->channel_id > 0) {
LOG_ERR("Only channel 0 is supported.");
return -EINVAL;
}
/* Check reference */
if (channel_cfg->reference != ADC_REF_INTERNAL) {
LOG_ERR("Selected ADC reference is not supported.");
return -EINVAL;
}
/* Check internal reference */
switch (config->vref_internal_mv) {
case 900:
vref_internal_mv = ADC_VREF_0P9V;
break;
case 1200:
vref_internal_mv = ADC_VREF_1P2V;
break;
default:
LOG_ERR("Selected reference voltage is not supported.");
return -EINVAL;
}
/* Check sample frequency */
switch (config->sample_freq) {
case 23000:
sample_freq = ADC_SAMPLE_FREQ_23K;
break;
case 48000:
sample_freq = ADC_SAMPLE_FREQ_48K;
break;
case 96000:
sample_freq = ADC_SAMPLE_FREQ_96K;
break;
default:
LOG_ERR("Selected sample frequency is not supported.");
return -EINVAL;
}
/* Check gain */
switch (channel_cfg->gain) {
case ADC_GAIN_1:
pre_scale = ADC_PRESCALE_1;
break;
case ADC_GAIN_1_4:
pre_scale = ADC_PRESCALE_1F4;
break;
default:
LOG_ERR("Selected ADC gain is not supported.");
return -EINVAL;
}
/* Check acquisition time */
switch (channel_cfg->acquisition_time) {
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 3):
sample_cycl = ADC_SAMPLE_CYC_3;
break;
case ADC_ACQ_TIME_DEFAULT:
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 6):
sample_cycl = ADC_SAMPLE_CYC_6;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 9):
sample_cycl = ADC_SAMPLE_CYC_9;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 12):
sample_cycl = ADC_SAMPLE_CYC_12;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 18):
sample_cycl = ADC_SAMPLE_CYC_18;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 24):
sample_cycl = ADC_SAMPLE_CYC_24;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 36):
sample_cycl = ADC_SAMPLE_CYC_36;
break;
case ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 48):
sample_cycl = ADC_SAMPLE_CYC_48;
break;
default:
LOG_ERR("Selected ADC acquisition time is not supported.");
return -EINVAL;
}
/* Check for valid pins configuration */
input_positive = adc_b91_get_pin(channel_cfg->input_positive);
input_negative = adc_b91_get_pin(channel_cfg->input_negative);
if ((input_positive == (uint8_t)ADC_VBAT || input_negative == (uint8_t)ADC_VBAT) &&
channel_cfg->differential) {
LOG_ERR("VBAT pin is not available for differential mode.");
return -EINVAL;
} else if (channel_cfg->differential && (input_negative == (uint8_t)NOINPUTN)) {
LOG_ERR("Negative input is not selected.");
return -EINVAL;
}
/* Init ADC */
data->differential = channel_cfg->differential;
adc_init(vref_internal_mv, pre_scale, sample_freq);
adc_set_vbat_divider(ADC_VBAT_DIV_OFF);
adc_set_tsample_cycle(sample_cycl);
/* Init ADC Pins */
if (channel_cfg->differential) {
/* Differential pins configuration */
adc_pin_config(ADC_GPIO_MODE, input_positive);
adc_pin_config(ADC_GPIO_MODE, input_negative);
adc_set_diff_input(channel_cfg->input_positive, channel_cfg->input_negative);
} else if (input_positive == (uint8_t)ADC_VBAT) {
/* Single-ended Vbat pin configuration */
adc_set_diff_input(ADC_VBAT, GND);
} else {
/* Single-ended GPIO pin configuration */
adc_pin_config(ADC_GPIO_MODE, input_positive);
adc_set_diff_input(channel_cfg->input_positive, GND);
}
return 0;
}
/* API implementation: read */
static int adc_b91_read(const struct device *dev,
const struct adc_sequence *sequence)
{
int status;
struct b91_adc_data *data = dev->data;
adc_context_lock(&data->ctx, false, NULL);
status = adc_b91_adc_start_read(dev, sequence);
adc_context_release(&data->ctx, status);
return status;
}
#ifdef CONFIG_ADC_ASYNC
/* API implementation: read_async */
static int adc_b91_read_async(const struct device *dev,
const struct adc_sequence *sequence,
struct k_poll_signal *async)
{
int status;
struct b91_adc_data *data = dev->data;
adc_context_lock(&data->ctx, true, async);
status = adc_b91_adc_start_read(dev, sequence);
adc_context_release(&data->ctx, status);
return status;
}
#endif /* CONFIG_ADC_ASYNC */
static struct b91_adc_data data_0 = {
ADC_CONTEXT_INIT_TIMER(data_0, ctx),
ADC_CONTEXT_INIT_LOCK(data_0, ctx),
ADC_CONTEXT_INIT_SYNC(data_0, ctx),
};
static const struct b91_adc_cfg cfg_0 = {
.sample_freq = DT_INST_PROP(0, sample_freq),
.vref_internal_mv = DT_INST_PROP(0, vref_internal_mv),
};
static DEVICE_API(adc, adc_b91_driver_api) = {
.channel_setup = adc_b91_channel_setup,
.read = adc_b91_read,
#ifdef CONFIG_ADC_ASYNC
.read_async = adc_b91_read_async,
#endif
.ref_internal = cfg_0.vref_internal_mv,
};
DEVICE_DT_INST_DEFINE(0, adc_b91_init, NULL,
&data_0, &cfg_0,
POST_KERNEL,
CONFIG_ADC_INIT_PRIORITY,
&adc_b91_driver_api);