Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | /*
* Copyright (c) 2023 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_smartbond_adc
#define ADC_CONTEXT_USES_KERNEL_TIMER
#include <DA1469xAB.h>
#include "adc_context.h"
#include <zephyr/dt-bindings/adc/smartbond-adc.h>
#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
#include <zephyr/sys/math_extras.h>
#include <zephyr/drivers/pinctrl.h>
LOG_MODULE_REGISTER(adc_smartbond_adc);
struct adc_smartbond_cfg {
const struct pinctrl_dev_config *pcfg;
};
struct adc_smartbond_data {
struct adc_context ctx;
/* Buffer to store channel data */
uint16_t *buffer;
/* Copy of channel mask from sequence */
uint32_t channel_read_mask;
/* Number of bits in sequence channels */
uint8_t sequence_channel_count;
/* Index in buffer to store current value to */
uint8_t result_index;
};
#define SMARTBOND_ADC_CHANNEL_COUNT 8
/*
* Channels are handled by software this array holds individual
* settings for each channel that must be applied before conversion.
*/
struct adc_smartbond_channel_cfg {
uint32_t gp_adc_ctrl_reg;
uint32_t gp_adc_ctrl2_reg;
} m_channels[SMARTBOND_ADC_CHANNEL_COUNT];
/* Implementation of the ADC driver API function: adc_channel_setup. */
static int adc_smartbond_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
uint8_t channel_id = channel_cfg->channel_id;
struct adc_smartbond_channel_cfg *config = &m_channels[channel_id];
if (channel_id >= SMARTBOND_ADC_CHANNEL_COUNT) {
return -EINVAL;
}
if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) {
LOG_ERR("Selected ADC acquisition time is not valid");
return -EINVAL;
}
if (channel_cfg->differential) {
if (channel_cfg->input_positive != SMARTBOND_GPADC_P1_09 &&
channel_cfg->input_positive != SMARTBOND_GPADC_P0_08) {
LOG_ERR("Differential channels supported only for P1_09 and P0_08");
return -EINVAL;
}
}
switch (channel_cfg->gain) {
case ADC_GAIN_1_3:
/* Turn on attenuator and increase sample time to 32 cycles */
config->gp_adc_ctrl2_reg = 0x101;
break;
case ADC_GAIN_1:
config->gp_adc_ctrl2_reg = 0;
break;
default:
LOG_ERR("Selected ADC gain is not valid");
return -EINVAL;
}
switch (channel_cfg->reference) {
case ADC_REF_INTERNAL:
break;
default:
LOG_ERR("Selected ADC reference is not valid");
return -EINVAL;
}
config->gp_adc_ctrl_reg =
channel_cfg->input_positive << GPADC_GP_ADC_CTRL_REG_GP_ADC_SEL_Pos;
if (!channel_cfg->differential) {
config->gp_adc_ctrl_reg |= GPADC_GP_ADC_CTRL_REG_GP_ADC_SE_Msk;
}
return 0;
}
#define PER_CHANNEL_ADC_CONFIG_MASK (GPADC_GP_ADC_CTRL_REG_GP_ADC_SEL_Msk | \
GPADC_GP_ADC_CTRL_REG_GP_ADC_SE_Msk)
static int pop_count(uint32_t n)
{
return __builtin_popcount(n);
}
static void adc_context_start_sampling(struct adc_context *ctx)
{
uint32_t val;
struct adc_smartbond_data *data =
CONTAINER_OF(ctx, struct adc_smartbond_data, ctx);
/* Extract lower channel from sequence mask */
int current_channel = u32_count_trailing_zeros(data->channel_read_mask);
if (ctx->sequence.calibrate) {
/* TODO: Add calibration code */
} else {
val = GPADC->GP_ADC_CTRL_REG & ~PER_CHANNEL_ADC_CONFIG_MASK;
val |= m_channels[current_channel].gp_adc_ctrl_reg;
val |= GPADC_GP_ADC_CTRL_REG_GP_ADC_START_Msk |
GPADC_GP_ADC_CTRL_REG_GP_ADC_MINT_Msk;
GPADC->GP_ADC_CTRL2_REG = m_channels[current_channel].gp_adc_ctrl2_reg;
GPADC->GP_ADC_CTRL_REG = val;
}
}
static void adc_context_update_buffer_pointer(struct adc_context *ctx,
bool repeat)
{
struct adc_smartbond_data *data =
CONTAINER_OF(ctx, struct adc_smartbond_data, ctx);
if (!repeat) {
data->buffer += data->sequence_channel_count;
}
}
static int check_buffer_size(const struct adc_sequence *sequence,
uint8_t active_channels)
{
size_t needed_buffer_size;
needed_buffer_size = active_channels * sizeof(uint16_t);
if (sequence->options) {
needed_buffer_size *= (1 + 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(const struct device *dev,
const struct adc_sequence *sequence)
{
int error;
struct adc_smartbond_data *data = dev->data;
if (sequence->oversampling > 7U) {
LOG_ERR("Invalid oversampling");
return -EINVAL;
}
if ((sequence->channels == 0) ||
((sequence->channels & ~BIT_MASK(SMARTBOND_ADC_CHANNEL_COUNT)) != 0)) {
LOG_ERR("Channel scanning is not supported");
return -EINVAL;
}
if (sequence->resolution < 8 || sequence->resolution > 15) {
LOG_ERR("ADC resolution value %d is not valid",
sequence->resolution);
return -EINVAL;
}
error = check_buffer_size(sequence, 1);
if (error) {
return error;
}
data->buffer = sequence->buffer;
data->channel_read_mask = sequence->channels;
data->sequence_channel_count = pop_count(sequence->channels);
data->result_index = 0;
adc_context_start_read(&data->ctx, sequence);
error = adc_context_wait_for_completion(&data->ctx);
return error;
}
static void adc_smartbond_isr(const struct device *dev)
{
struct adc_smartbond_data *data = dev->data;
int current_channel = u32_count_trailing_zeros(data->channel_read_mask);
GPADC->GP_ADC_CLEAR_INT_REG = 0;
/* Store current channel value, result is left justified, move bits right */
data->buffer[data->result_index++] = ((uint16_t)GPADC->GP_ADC_RESULT_REG) >>
(16 - data->ctx.sequence.resolution);
/* Exclude channel from mask for further reading */
data->channel_read_mask ^= 1 << current_channel;
if (data->channel_read_mask == 0) {
adc_context_on_sampling_done(&data->ctx, dev);
} else {
adc_context_start_sampling(&data->ctx);
}
LOG_DBG("%s ISR triggered.", dev->name);
}
/* Implementation of the ADC driver API function: adc_read. */
static int adc_smartbond_read(const struct device *dev,
const struct adc_sequence *sequence)
{
int error;
struct adc_smartbond_data *data = dev->data;
adc_context_lock(&data->ctx, false, NULL);
error = start_read(dev, sequence);
adc_context_release(&data->ctx, error);
return error;
}
#if defined(CONFIG_ADC_ASYNC)
/* Implementation of the ADC driver API function: adc_read_sync. */
static int adc_smartbond_read_async(const struct device *dev,
const struct adc_sequence *sequence,
struct k_poll_signal *async)
{
struct adc_smartbond_data *data = dev->data;
int error;
adc_context_lock(&data->ctx, true, async);
error = start_read(dev, sequence);
adc_context_release(&data->ctx, error);
return error;
}
#endif /* CONFIG_ADC_ASYNC */
static int adc_smartbond_init(const struct device *dev)
{
int err;
struct adc_smartbond_data *data = dev->data;
const struct adc_smartbond_cfg *config = dev->config;
GPADC->GP_ADC_CTRL_REG = GPADC_GP_ADC_CTRL_REG_GP_ADC_EN_Msk;
GPADC->GP_ADC_CTRL2_REG = 0;
GPADC->GP_ADC_CTRL3_REG = 0x40;
GPADC->GP_ADC_CLEAR_INT_REG = 0x0;
/*
* Configure dt provided device signals when available.
* pinctrl is optional so ENOENT is not setup failure.
*/
err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (err < 0 && err != -ENOENT) {
LOG_ERR("ADC pinctrl setup failed (%d)", err);
return err;
}
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
adc_smartbond_isr, DEVICE_DT_INST_GET(0), 0);
NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
NVIC_EnableIRQ(DT_INST_IRQN(0));
adc_context_unlock_unconditionally(&data->ctx);
return 0;
}
static const struct adc_driver_api adc_smartbond_driver_api = {
.channel_setup = adc_smartbond_channel_setup,
.read = adc_smartbond_read,
#ifdef CONFIG_ADC_ASYNC
.read_async = adc_smartbond_read_async,
#endif
.ref_internal = 1200,
};
/*
* There is only one instance on supported SoCs, so inst is guaranteed
* to be 0 if any instance is okay. (We use adc_0 above, so the driver
* is relying on the numeric instance value in a way that happens to
* be safe.)
*
* Just in case that assumption becomes invalid in the future, we use
* a BUILD_ASSERT().
*/
#define ADC_INIT(inst) \
BUILD_ASSERT((inst) == 0, \
"multiple instances not supported"); \
PINCTRL_DT_INST_DEFINE(inst); \
static const struct adc_smartbond_cfg adc_smartbond_cfg_##inst = {\
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
}; \
static struct adc_smartbond_data adc_smartbond_data_##inst = { \
ADC_CONTEXT_INIT_TIMER(adc_smartbond_data_##inst, ctx), \
ADC_CONTEXT_INIT_LOCK(adc_smartbond_data_##inst, ctx), \
ADC_CONTEXT_INIT_SYNC(adc_smartbond_data_##inst, ctx), \
}; \
DEVICE_DT_INST_DEFINE(0, \
adc_smartbond_init, NULL, \
&adc_smartbond_data_##inst, \
&adc_smartbond_cfg_##inst, \
POST_KERNEL, \
CONFIG_ADC_INIT_PRIORITY, \
&adc_smartbond_driver_api);
DT_INST_FOREACH_STATUS_OKAY(ADC_INIT)
|