From ee6ec8d4f3a994e93bfcdac63bc2c7fd3f8baed3 Mon Sep 17 00:00:00 2001 From: Pavlo Hamov Date: Tue, 2 Feb 2021 18:22:09 +0200 Subject: [PATCH] drivers: adc: cc32xx: Add support Support 4 channels in IRQ mode. Sync/Async Signed-off-by: Pavlo Hamov --- drivers/adc/CMakeLists.txt | 1 + drivers/adc/Kconfig | 2 + drivers/adc/Kconfig.cc32xx | 11 + drivers/adc/adc_cc32xx.c | 327 ++++++++++++++++++++++++++++ dts/arm/ti/cc32xx.dtsi | 17 ++ dts/bindings/adc/ti,cc32xx-adc.yaml | 21 ++ 6 files changed, 379 insertions(+) create mode 100644 drivers/adc/Kconfig.cc32xx create mode 100644 drivers/adc/adc_cc32xx.c create mode 100644 dts/bindings/adc/ti,cc32xx-adc.yaml diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 6ebef6f14c..d999c5105b 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -17,3 +17,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_LMP90XXX adc_lmp90xxx.c) zephyr_library_sources_ifdef(CONFIG_ADC_MCP320X adc_mcp320x.c) zephyr_library_sources_ifdef(CONFIG_ADC_NPCX adc_npcx.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE adc_handlers.c) +zephyr_library_sources_ifdef(CONFIG_ADC_CC32XX adc_cc32xx.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 9637eb9b2d..7d3ff92cfb 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -56,4 +56,6 @@ source "drivers/adc/Kconfig.mcp320x" source "drivers/adc/Kconfig.npcx" +source "drivers/adc/Kconfig.cc32xx" + endif # ADC diff --git a/drivers/adc/Kconfig.cc32xx b/drivers/adc/Kconfig.cc32xx new file mode 100644 index 0000000000..816338a943 --- /dev/null +++ b/drivers/adc/Kconfig.cc32xx @@ -0,0 +1,11 @@ +# Copyright (c) 2021 Pavlo Hamov +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_TI_CC32XX_ADC := ti,cc32xx-adc + +config ADC_CC32XX + bool "CC32XX ADC driver" + depends on SOC_SERIES_CC32XX && SOC_FAMILY_TISIMPLELINK + default $(dt_compat_enabled,$(DT_COMPAT_TI_CC32XX_ADC)) + help + This option enables the CC32XX ADC driver. diff --git a/drivers/adc/adc_cc32xx.c b/drivers/adc/adc_cc32xx.c new file mode 100644 index 0000000000..696604179f --- /dev/null +++ b/drivers/adc/adc_cc32xx.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2021 Pavlo Hamov + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_cc32xx_adc + +#include + +#include +#include +#include +#include +#include + +/* Driverlib includes */ +#include +#include +#include +#include +#include +#include + +#define CHAN_COUNT 4 + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL +#include +LOG_MODULE_REGISTER(adc_cc32xx); + +#define ISR_MASK (ADC_DMA_DONE | ADC_FIFO_OVERFLOW | ADC_FIFO_UNDERFLOW \ + | ADC_FIFO_EMPTY | ADC_FIFO_FULL) + +struct adc_cc32xx_data { + struct adc_context ctx; + const struct device *dev; + uint16_t *buffer; + uint16_t *repeat_buffer; + uint32_t channels; + uint8_t offset[CHAN_COUNT]; + size_t active_channels; +}; + +struct adc_cc32xx_cfg { + unsigned long base; + void (*irq_cfg_func)(void); +}; + +static const int s_chPin[CHAN_COUNT] = { + PIN_57, + PIN_58, + PIN_59, + PIN_60, +}; + +static const int s_channel[CHAN_COUNT] = { + ADC_CH_0, + ADC_CH_1, + ADC_CH_2, + ADC_CH_3, +}; + +static inline void start_sampling(unsigned long base, int ch) +{ + MAP_ADCChannelEnable(base, ch); + for (int i = 0; i < 5; i++) { + while (!MAP_ADCFIFOLvlGet(base, ch)) { + } + MAP_ADCFIFORead(base, ch); + } + MAP_ADCIntClear(base, ch, ISR_MASK); + MAP_ADCIntEnable(base, ch, ISR_MASK); +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct adc_cc32xx_data *data = + CONTAINER_OF(ctx, struct adc_cc32xx_data, ctx); + const struct adc_cc32xx_cfg *config = data->dev->config; + + data->channels = ctx->sequence.channels; + data->repeat_buffer = data->buffer; + + for (int i = 0; i < CHAN_COUNT; ++i) { + if (ctx->sequence.channels & BIT(i)) { + start_sampling(config->base, s_channel[i]); + } + } +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat) +{ + struct adc_cc32xx_data *data = + CONTAINER_OF(ctx, struct adc_cc32xx_data, ctx); + + if (repeat) { + data->buffer = data->repeat_buffer; + } else { + data->buffer += data->active_channels; + } +} + +static int adc_cc32xx_init(const struct device *dev) +{ + struct adc_cc32xx_data *data = dev->data; + const struct adc_cc32xx_cfg *config = dev->config; + + data->dev = dev; + + LOG_DBG("Initializing...."); + + for (int i = 0; i < CHAN_COUNT; ++i) { + const int ch = s_channel[i]; + + MAP_ADCIntDisable(config->base, ch, ISR_MASK); + MAP_ADCChannelDisable(config->base, ch); + MAP_ADCDMADisable(config->base, ch); + MAP_ADCIntClear(config->base, ch, ISR_MASK); + } + MAP_ADCEnable(config->base); + config->irq_cfg_func(); + + adc_context_unlock_unconditionally(&data->ctx); + return 0; +} + +static int adc_cc32xx_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + const struct adc_cc32xx_cfg *config = dev->config; + const uint8_t ch = channel_cfg->channel_id; + + if (ch >= CHAN_COUNT) { + LOG_ERR("Channel %d is not supported, max %d", ch, CHAN_COUNT); + return -EINVAL; + } + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Acquisition time is not valid"); + return -EINVAL; + } + + if (channel_cfg->differential) { + LOG_ERR("Differential channels are not supported"); + return -EINVAL; + } + + if (channel_cfg->gain != ADC_GAIN_1) { + LOG_ERR("Gain is not valid"); + return -EINVAL; + } + + if (channel_cfg->reference != ADC_REF_INTERNAL) { + LOG_ERR("Reference is not valid"); + return -EINVAL; + } + + LOG_DBG("Setup %d", ch); + + MAP_ADCChannelDisable(config->base, s_channel[ch]); + MAP_ADCIntDisable(config->base, s_channel[ch], ISR_MASK); + MAP_PinDirModeSet(s_chPin[ch], PIN_DIR_MODE_IN); + MAP_PinTypeADC(s_chPin[ch], PIN_MODE_255); + + return 0; +} + +static int cc32xx_read(const struct device *dev, + const struct adc_sequence *sequence, + bool asynchronous, + struct k_poll_signal *sig) +{ + struct adc_cc32xx_data *data = dev->data; + int rv; + size_t exp_size; + + if (sequence->resolution != 12) { + LOG_ERR("Only 12 Resolution is supported, but %d got", + sequence->resolution); + return -EINVAL; + } + + data->active_channels = 0; + for (int i = 0; i < CHAN_COUNT; ++i) { + if (!(sequence->channels & BIT(i))) { + continue; + } + data->offset[i] = data->active_channels++; + } + exp_size = data->active_channels * sizeof(uint16_t); + if (sequence->options) { + exp_size *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < exp_size) { + LOG_ERR("Required buffer size is %u, but %u got", + exp_size, sequence->buffer_size); + return -ENOMEM; + } + + data->buffer = sequence->buffer; + + adc_context_lock(&data->ctx, asynchronous, sig); + adc_context_start_read(&data->ctx, sequence); + rv = adc_context_wait_for_completion(&data->ctx); + adc_context_release(&data->ctx, rv); + return rv; +} + +static int adc_cc32xx_read(const struct device *dev, + const struct adc_sequence *sequence) +{ + return cc32xx_read(dev, sequence, false, NULL); +} + +#ifdef CONFIG_ADC_ASYNC +static int adc_cc32xx_read_async(const struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + return cc32xx_read(dev, sequence, true, async); +} +#endif + +static void adc_cc32xx_isr(const struct device *dev, int no) +{ + const struct adc_cc32xx_cfg *config = dev->config; + struct adc_cc32xx_data *data = (struct adc_cc32xx_data *)dev->data; + const int chan = s_channel[no]; + unsigned long mask = MAP_ADCIntStatus(config->base, chan); + int cnt = 0; + int rv = 0; + + MAP_ADCIntClear(config->base, chan, mask); + + if ((mask & ADC_FIFO_EMPTY) || !(mask & ADC_FIFO_FULL)) { + return; + } + + while (MAP_ADCFIFOLvlGet(config->base, chan)) { + rv += (MAP_ADCFIFORead(config->base, chan) >> 2) & 0x0FFF; + cnt++; + } + + *(data->buffer + data->offset[no]) = rv / cnt; + data->channels &= ~BIT(no); + + MAP_ADCIntDisable(config->base, chan, ISR_MASK); + MAP_ADCChannelDisable(config->base, chan); + + LOG_DBG("ISR %d, 0x%lX %d %d", chan, mask, rv, cnt); + if (!data->channels) { + adc_context_on_sampling_done(&data->ctx, dev); + } +} + +static void adc_cc32xx_isr_ch0(const struct device *dev) +{ + adc_cc32xx_isr(dev, 0); +} + +static void adc_cc32xx_isr_ch1(const struct device *dev) +{ + adc_cc32xx_isr(dev, 1); +} + +static void adc_cc32xx_isr_ch2(const struct device *dev) +{ + adc_cc32xx_isr(dev, 2); +} + +static void adc_cc32xx_isr_ch3(const struct device *dev) +{ + adc_cc32xx_isr(dev, 3); +} + +static const struct adc_driver_api cc32xx_driver_api = { + .channel_setup = adc_cc32xx_channel_setup, + .read = adc_cc32xx_read, +#ifdef CONFIG_ADC_ASYNC + .read_async = adc_cc32xx_read_async, +#endif + .ref_internal = 1467, +}; + +#define cc32xx_ADC_IRQ_CONNECT(index, chan) \ + do { \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(index, chan, irq), \ + DT_INST_IRQ_BY_IDX(index, chan, priority), \ + adc_cc32xx_isr_ch##chan, \ + DEVICE_DT_INST_GET(index), 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(index, chan, irq)); \ + } while (0) + +#define cc32xx_ADC_INIT(index) \ + \ + static void adc_cc32xx_cfg_func_##index(void); \ + \ + static const struct adc_cc32xx_cfg adc_cc32xx_cfg_##index = { \ + .base = DT_INST_REG_ADDR(index), \ + .irq_cfg_func = adc_cc32xx_cfg_func_##index, \ + }; \ + static struct adc_cc32xx_data adc_cc32xx_data_##index = { \ + ADC_CONTEXT_INIT_TIMER(adc_cc32xx_data_##index, ctx), \ + ADC_CONTEXT_INIT_LOCK(adc_cc32xx_data_##index, ctx), \ + ADC_CONTEXT_INIT_SYNC(adc_cc32xx_data_##index, ctx), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(index, \ + &adc_cc32xx_init, device_pm_control_nop, \ + &adc_cc32xx_data_##index, &adc_cc32xx_cfg_##index, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ + &cc32xx_driver_api); \ + \ + static void adc_cc32xx_cfg_func_##index(void) \ + { \ + cc32xx_ADC_IRQ_CONNECT(index, 0); \ + cc32xx_ADC_IRQ_CONNECT(index, 1); \ + cc32xx_ADC_IRQ_CONNECT(index, 2); \ + cc32xx_ADC_IRQ_CONNECT(index, 3); \ + } + +DT_INST_FOREACH_STATUS_OKAY(cc32xx_ADC_INIT) diff --git a/dts/arm/ti/cc32xx.dtsi b/dts/arm/ti/cc32xx.dtsi index 16f7e2b297..4c85e7cc96 100644 --- a/dts/arm/ti/cc32xx.dtsi +++ b/dts/arm/ti/cc32xx.dtsi @@ -8,12 +8,20 @@ #define INT_UARTA0 21 // UART0 Rx and Tx #define INT_UARTA1 22 // UART1 Rx and Tx #define INT_I2CA0 24 // I2C controller +#define INT_ADCCH0 30 // ADC channel 0 +#define INT_ADCCH1 31 // ADC channel 1 +#define INT_ADCCH2 32 // ADC channel 2 +#define INT_ADCCH3 33 // ADC channel 3 /* Note: Zephyr uses exception numbers, vs the IRQ #s used by the CC32XX SDK */ /* which are offset by 16: */ #define EXP_UARTA0 (INT_UARTA0 - 16) #define EXP_UARTA1 (INT_UARTA1 - 16) #define EXP_I2CA0 (INT_I2CA0 - 16) +#define EXP_ADCCH0 (INT_ADCCH0 - 16) +#define EXP_ADCCH1 (INT_ADCCH1 - 16) +#define EXP_ADCCH2 (INT_ADCCH2 - 16) +#define EXP_ADCCH3 (INT_ADCCH3 - 16) #define EXC_GPIOA0 0 /* (INT_GPIOA0 - 16) = (16-16) */ #define EXC_GPIOA1 1 /* (INT_GPIOA1 - 16) = (17-16) */ #define EXC_GPIOA2 2 /* (INT_GPIOA2 - 16) = (18-16) */ @@ -111,6 +119,15 @@ gpio-controller; #gpio-cells = <2>; }; + + adc0: adc@4402e800 { + compatible = "ti,cc32xx-adc"; + reg = <0x4402E800 0x100>; + interrupts = , , , ; + status = "disabled"; + label = "ADC_0"; + #io-channel-cells = <1>; + }; }; }; diff --git a/dts/bindings/adc/ti,cc32xx-adc.yaml b/dts/bindings/adc/ti,cc32xx-adc.yaml new file mode 100644 index 0000000000..9a8457f328 --- /dev/null +++ b/dts/bindings/adc/ti,cc32xx-adc.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2021 Pavlo Hamov +# SPDX-License-Identifier: Apache-2.0 + +description: cc32xx ADC node + +compatible: "ti,cc32xx-adc" + +include: adc-controller.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input