/* * Copyright (c) 2023 Caspar Friedrich * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #define ADC_CONTEXT_USES_KERNEL_TIMER /* * This requires to be included _after_ `#define ADC_CONTEXT_USES_KERNEL_TIMER` */ #include "adc_context.h" #define DT_DRV_COMPAT ti_tla2021 LOG_MODULE_REGISTER(tla2021, CONFIG_ADC_LOG_LEVEL); #define ACQ_THREAD_PRIORITY CONFIG_ADC_TLA2021_ACQUISITION_THREAD_PRIORITY #define ACQ_THREAD_STACK_SIZE CONFIG_ADC_TLA2021_ACQUISITION_THREAD_STACK_SIZE #define ADC_CHANNEL_msk BIT(0) #define ADC_RESOLUTION 12 /* * Conversion Data Register (RP = 00h) [reset = 0000h] */ #define REG_DATA 0x00 #define REG_DATA_pos 4 /* * Configuration Register (RP = 01h) [reset = 8583h] */ #define REG_CONFIG 0x01 #define REG_CONFIG_DEFAULT 0x8583 #define REG_CONFIG_DR_pos 5 #define REG_CONFIG_MODE_pos 8 #define REG_CONFIG_PGA_pos 9 /* TLA2022 and TLA2024 Only */ #define REG_CONFIG_MUX_pos 12 /* TLA2024 Only */ #define REG_CONFIG_OS_pos 15 #define REG_CONFIG_OS_msk (BIT_MASK(1) << REG_CONFIG_OS_pos) typedef int16_t tla2021_reg_data_t; typedef uint16_t tla2021_reg_config_t; struct tla2021_config { const struct i2c_dt_spec bus; k_tid_t acq_thread_id; }; struct tla2021_data { const struct device *dev; struct adc_context ctx; struct k_sem acq_lock; tla2021_reg_data_t *buffer; tla2021_reg_data_t *repeat_buffer; /* * Shadow register */ tla2021_reg_config_t reg_config; }; static int tla2021_read_register(const struct device *dev, uint8_t reg, uint16_t *value) { int ret; const struct tla2021_config *config = dev->config; uint8_t tmp[2]; ret = i2c_write_read_dt(&config->bus, ®, sizeof(reg), tmp, sizeof(tmp)); if (ret) { return ret; } *value = sys_get_be16(tmp); return 0; } static int tla2021_write_register(const struct device *dev, uint8_t reg, uint16_t value) { int ret; const struct tla2021_config *config = dev->config; uint8_t tmp[3] = {reg}; sys_put_be16(value, &tmp[1]); ret = i2c_write_dt(&config->bus, tmp, sizeof(tmp)); if (ret) { return ret; } return 0; } static int tla2021_channel_setup(const struct device *dev, const struct adc_channel_cfg *cfg) { if (cfg->gain != ADC_GAIN_1) { LOG_ERR("Invalid gain"); return -EINVAL; } if (cfg->reference != ADC_REF_INTERNAL) { LOG_ERR("Invalid reference"); return -EINVAL; } if (cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { LOG_ERR("Invalid acquisition time"); return -EINVAL; } return 0; } static int tla2021_start_read(const struct device *dev, const struct adc_sequence *seq) { struct tla2021_data *data = dev->data; const size_t num_extra_samples = seq->options ? seq->options->extra_samplings : 0; const size_t num_samples = (1 + num_extra_samples) * POPCOUNT(seq->channels); if (!(seq->channels & ADC_CHANNEL_msk)) { LOG_ERR("Selected channel(s) not supported: %x", seq->channels); return -EINVAL; } if (seq->resolution != ADC_RESOLUTION) { LOG_ERR("Selected resolution not supported: %d", seq->resolution); return -EINVAL; } if (seq->oversampling) { LOG_ERR("Oversampling is not supported"); return -EINVAL; } if (seq->calibrate) { LOG_ERR("Calibration is not supported"); return -EINVAL; } if (!seq->buffer) { LOG_ERR("Buffer invalid"); return -EINVAL; } if (seq->buffer_size < (num_samples * sizeof(tla2021_reg_data_t))) { LOG_ERR("buffer size too small"); return -EINVAL; } data->buffer = seq->buffer; adc_context_start_read(&data->ctx, seq); return adc_context_wait_for_completion(&data->ctx); } static int tla2021_read_async(const struct device *dev, const struct adc_sequence *seq, struct k_poll_signal *async) { int ret; struct tla2021_data *data = dev->data; adc_context_lock(&data->ctx, async ? true : false, async); ret = tla2021_start_read(dev, seq); adc_context_release(&data->ctx, ret); return ret; } static int tla2021_read(const struct device *dev, const struct adc_sequence *seq) { return tla2021_read_async(dev, seq, NULL); } static void adc_context_start_sampling(struct adc_context *ctx) { int ret; struct tla2021_data *data = CONTAINER_OF(ctx, struct tla2021_data, ctx); const struct device *dev = data->dev; tla2021_reg_config_t reg = data->reg_config; /* * Start single-shot conversion */ WRITE_BIT(reg, REG_CONFIG_MODE_pos, 1); WRITE_BIT(reg, REG_CONFIG_OS_pos, 1); ret = tla2021_write_register(dev, REG_CONFIG, reg); if (ret) { LOG_WRN("Failed to start conversion"); } data->repeat_buffer = data->buffer; k_sem_give(&data->acq_lock); } static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) { struct tla2021_data *data = CONTAINER_OF(ctx, struct tla2021_data, ctx); if (repeat_sampling) { data->buffer = data->repeat_buffer; } } static void tla2021_acq_thread_fn(void *p1, void *p2, void *p3) { int ret; struct tla2021_data *data = p1; const struct device *dev = data->dev; while (true) { k_sem_take(&data->acq_lock, K_FOREVER); tla2021_reg_config_t reg; tla2021_reg_data_t res; /* * Wait until sampling is done */ do { ret = tla2021_read_register(dev, REG_CONFIG, ®); if (ret < 0) { adc_context_complete(&data->ctx, ret); } } while (!(reg & REG_CONFIG_OS_msk)); /* * Read result */ ret = tla2021_read_register(dev, REG_DATA, &res); if (ret) { adc_context_complete(&data->ctx, ret); } /* * ADC data is stored in the upper 12 bits */ res >>= REG_DATA_pos; *data->buffer++ = res; adc_context_on_sampling_done(&data->ctx, data->dev); } } static int tla2021_init(const struct device *dev) { int ret; const struct tla2021_config *config = dev->config; struct tla2021_data *data = dev->data; k_sem_init(&data->acq_lock, 0, 1); if (!i2c_is_ready_dt(&config->bus)) { LOG_ERR("Bus not ready"); return -EINVAL; } ret = tla2021_write_register(dev, REG_CONFIG, data->reg_config); if (ret) { LOG_ERR("Device reset failed: %d", ret); return ret; } adc_context_unlock_unconditionally(&data->ctx); return 0; } static const struct adc_driver_api tla2021_driver_api = { .channel_setup = tla2021_channel_setup, .read = tla2021_read, .ref_internal = 2048, #ifdef CONFIG_ADC_ASYNC .read_async = tla2021_read_async, #endif }; #define TLA2021_INIT(n) \ static const struct tla2021_config inst_##n##_config; \ static struct tla2021_data inst_##n##_data; \ K_THREAD_DEFINE(inst_##n##_thread, ACQ_THREAD_STACK_SIZE, tla2021_acq_thread_fn, \ &inst_##n##_data, NULL, NULL, ACQ_THREAD_PRIORITY, 0, 0); \ static const struct tla2021_config inst_##n##_config = { \ .bus = I2C_DT_SPEC_INST_GET(n), \ .acq_thread_id = inst_##n##_thread, \ }; \ static struct tla2021_data inst_##n##_data = { \ .dev = DEVICE_DT_INST_GET(n), \ ADC_CONTEXT_INIT_LOCK(inst_##n##_data, ctx), \ ADC_CONTEXT_INIT_TIMER(inst_##n##_data, ctx), \ ADC_CONTEXT_INIT_SYNC(inst_##n##_data, ctx), \ .reg_config = REG_CONFIG_DEFAULT, \ }; \ DEVICE_DT_INST_DEFINE(n, &tla2021_init, NULL, &inst_##n##_data, &inst_##n##_config, \ POST_KERNEL, CONFIG_ADC_TLA2021_INIT_PRIORITY, &tla2021_driver_api); DT_INST_FOREACH_STATUS_OKAY(TLA2021_INIT) BUILD_ASSERT(CONFIG_I2C_INIT_PRIORITY < CONFIG_ADC_TLA2021_INIT_PRIORITY);