From f809614136c748707d365a1342128e0158145d55 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Huu Date: Tue, 20 Jun 2023 14:05:07 +0700 Subject: [PATCH] drivers: adc: add NXP S32 ADC SAR driver Add support ADC SAR for NXP S32. ADC SAR diver support 3 group channels (precision, standard and external), run normal trigger in oneshot conversion mode with 2 callbacks normal end of conversion and normal end chain callbacks. An instance only run on 1 group channel and 1 kind of callback at the same time. Signed-off-by: Cong Nguyen Huu --- drivers/adc/CMakeLists.txt | 1 + drivers/adc/Kconfig | 2 + drivers/adc/Kconfig.nxp_s32 | 9 + drivers/adc/adc_nxp_s32_adc_sar.c | 446 ++++++++++++++++++++++++++ dts/bindings/adc/nxp,s32-adc-sar.yaml | 49 +++ 5 files changed, 507 insertions(+) create mode 100644 drivers/adc/Kconfig.nxp_s32 create mode 100644 drivers/adc/adc_nxp_s32_adc_sar.c create mode 100644 dts/bindings/adc/nxp,s32-adc-sar.yaml diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index daf2dff347e..f10326d4d19 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -43,3 +43,4 @@ zephyr_library_sources_ifdef(CONFIG_ADC_INFINEON_CAT1 adc_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_ADC_SMARTBOND_GPADC adc_smartbond_gpadc.c) zephyr_library_sources_ifdef(CONFIG_ADC_SMARTBOND_SDADC adc_smartbond_sdadc.c) zephyr_library_sources_ifdef(CONFIG_ADC_TLA2021 adc_tla2021.c) +zephyr_library_sources_ifdef(CONFIG_ADC_NXP_S32_ADC_SAR adc_nxp_s32_adc_sar.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index 48c57c71a35..a915fa18fc0 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -110,4 +110,6 @@ source "drivers/adc/Kconfig.smartbond" source "drivers/adc/Kconfig.tla2021" +source "drivers/adc/Kconfig.nxp_s32" + endif # ADC diff --git a/drivers/adc/Kconfig.nxp_s32 b/drivers/adc/Kconfig.nxp_s32 new file mode 100644 index 00000000000..e57d21a9a0f --- /dev/null +++ b/drivers/adc/Kconfig.nxp_s32 @@ -0,0 +1,9 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config ADC_NXP_S32_ADC_SAR + bool "NXP S32 ADC SAR driver" + default y + depends on DT_HAS_NXP_S32_ADC_SAR_ENABLED + help + This option enables the NXP S32 ADC SAR driver. diff --git a/drivers/adc/adc_nxp_s32_adc_sar.c b/drivers/adc/adc_nxp_s32_adc_sar.c new file mode 100644 index 00000000000..7cb5baf5b5c --- /dev/null +++ b/drivers/adc/adc_nxp_s32_adc_sar.c @@ -0,0 +1,446 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +#define DT_DRV_COMPAT nxp_s32_adc_sar +LOG_MODULE_REGISTER(adc_nxp_s32_adc_sar, CONFIG_ADC_LOG_LEVEL); + +/* Convert channel of group ADC to channel of physical ADC instance */ +#define ADC_NXP_S32_GROUPCHAN_2_PHYCHAN(group, channel) \ + (ADC_SAR_IP_HW_REG_SIZE * group + channel) + +struct adc_nxp_s32_config { + ADC_Type *base; + uint8_t instance; + uint8_t group_channel; + uint8_t callback_select; + Adc_Sar_Ip_ConfigType *adc_cfg; + void (*irq_config_func)(const struct device *dev); + const struct pinctrl_dev_config *pin_cfg; +}; + +struct adc_nxp_s32_data { + const struct device *dev; + struct adc_context ctx; + uint16_t *buffer; + uint16_t *buf_end; + uint16_t *repeat_buffer; + uint32_t mask_channels; + uint8_t num_channels; +}; + +static int adc_nxp_s32_init(const struct device *dev) +{ + const struct adc_nxp_s32_config *config = dev->config; + struct adc_nxp_s32_data *data = dev->data; + Adc_Sar_Ip_StatusType status; + /* This array shows max number of channels of each group */ + uint8_t map_chan_group[ADC_SAR_IP_INSTANCE_COUNT][ADC_SAR_IP_NUM_GROUP_CHAN] + = FEATURE_ADC_MAX_CHN_COUNT; + + data->num_channels = map_chan_group[config->instance][config->group_channel]; + + if (config->pin_cfg) { + if (pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT)) { + return -EIO; + } + } + + status = Adc_Sar_Ip_Init(config->instance, config->adc_cfg); + if (status) { + return -EIO; + } + +#if FEATURE_ADC_HAS_CALIBRATION + status = Adc_Sar_Ip_DoCalibration(config->instance); + if (status) { + return -EIO; + } +#endif + + Adc_Sar_Ip_EnableNotifications(config->instance, + config->callback_select ? + ADC_SAR_IP_NOTIF_FLAG_NORMAL_ENDCHAIN + : ADC_SAR_IP_NOTIF_FLAG_NORMAL_EOC); + + data->dev = dev; + config->irq_config_func(dev); + + adc_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static int adc_nxp_s32_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + struct adc_nxp_s32_data *data = dev->data; + + if (channel_cfg->channel_id >= data->num_channels) { + LOG_ERR("Channel %d is not valid", channel_cfg->channel_id); + return -EINVAL; + } + + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Unsupported channel acquisition time"); + return -ENOTSUP; + } + + if (channel_cfg->differential) { + LOG_ERR("Differential channels are not supported"); + return -ENOTSUP; + } + + if (channel_cfg->gain != ADC_GAIN_1) { + LOG_ERR("Unsupported channel gain %d", channel_cfg->gain); + return -ENOTSUP; + } + + if (channel_cfg->reference != ADC_REF_INTERNAL) { + LOG_ERR("Unsupported channel reference"); + return -ENOTSUP; + } + + return 0; +} + +static int adc_nxp_s32_validate_buffer_size(const struct device *dev, + const struct adc_sequence *sequence) +{ + uint8_t active_channels = 0; + size_t needed_size; + + active_channels = POPCOUNT(sequence->channels); + + needed_size = active_channels * sizeof(uint16_t); + if (sequence->options) { + needed_size *= (1 + sequence->options->extra_samplings); + } + + if (sequence->buffer_size < needed_size) { + return -ENOSPC; + } + + return 0; +} + +#if FEATURE_ADC_HAS_AVERAGING +static int adc_nxp_s32_set_averaging(const struct device *dev, uint8_t oversampling) +{ + const struct adc_nxp_s32_config *config = dev->config; + Adc_Sar_Ip_AvgSelectType avg_sel = ADC_SAR_IP_AVG_4_CONV; + bool avg_en = true; + + switch (oversampling) { + case 0: + avg_en = false; + break; + case 2: + avg_sel = ADC_SAR_IP_AVG_4_CONV; + break; + case 3: + avg_sel = ADC_SAR_IP_AVG_8_CONV; + break; + case 4: + avg_sel = ADC_SAR_IP_AVG_16_CONV; + break; + case 5: + avg_sel = ADC_SAR_IP_AVG_32_CONV; + break; + default: + LOG_ERR("Unsupported oversampling value"); + return -ENOTSUP; + } + Adc_Sar_Ip_SetAveraging(config->instance, avg_en, avg_sel); + + return 0; +} +#endif + +#if (ADC_SAR_IP_SET_RESOLUTION == STD_ON) +static int adc_nxp_s32_set_resolution(const struct device *dev, uint8_t adc_resol) +{ + const struct adc_nxp_s32_config *config = dev->config; + Adc_Sar_Ip_Resolution resolution; + + switch (adc_resol) { + case 8: + resolution = ADC_SAR_IP_RESOLUTION_8; + break; + case 10: + resolution = ADC_SAR_IP_RESOLUTION_10; + break; + case 12: + resolution = ADC_SAR_IP_RESOLUTION_12; + break; + case 14: + resolution = ADC_SAR_IP_RESOLUTION_14; + break; + default: + LOG_ERR("Unsupported resolution"); + return -ENOTSUP; + } + Adc_Sar_Ip_SetResolution(config->instance, resolution); + + return 0; +} +#endif + +static int adc_nxp_s32_start_read_async(const struct device *dev, + const struct adc_sequence *sequence) +{ + const struct adc_nxp_s32_config *config = dev->config; + struct adc_nxp_s32_data *data = dev->data; + int error; + uint32_t mask; + uint8_t channel; + + if (find_msb_set(sequence->channels) > data->num_channels) { + LOG_ERR("Channels out of bit map"); + return -EINVAL; + } + + error = adc_nxp_s32_validate_buffer_size(dev, sequence); + if (error) { + LOG_ERR("Buffer size isn't enough"); + return -EINVAL; + } + +#if FEATURE_ADC_HAS_AVERAGING + error = adc_nxp_s32_set_averaging(dev, sequence->oversampling); + if (error) { + return -ENOTSUP; + } +#else + if (sequence->oversampling) { + LOG_ERR("Oversampling can't be changed"); + return -ENOTSUP; + } +#endif + +#if (ADC_SAR_IP_SET_RESOLUTION == STD_ON) + error = adc_nxp_s32_set_resolution(dev, sequence->resolution); + if (error) { + return -ENOTSUP; + } +#else + if (sequence->resolution != ADC_SAR_IP_MAX_RESOLUTION) { + LOG_ERR("Resolution can't be changed"); + return -ENOTSUP; + } +#endif + + if (sequence->calibrate) { +#if FEATURE_ADC_HAS_CALIBRATION + error = Adc_Sar_Ip_DoCalibration(config->instance); + if (error) { + LOG_ERR("Error during calibration"); + return -EIO; + } +#else + LOG_ERR("Unsupported calibration"); + return -ENOTSUP; +#endif + } + + for (int i = 0; i < data->num_channels; i++) { + mask = (sequence->channels >> i) & 0x1; + channel = ADC_NXP_S32_GROUPCHAN_2_PHYCHAN(config->group_channel, i); + if (mask) { + Adc_Sar_Ip_EnableChannelNotifications(config->instance, + channel, ADC_SAR_IP_CHAN_NOTIF_EOC); + Adc_Sar_Ip_EnableChannel(config->instance, + ADC_SAR_IP_CONV_CHAIN_NORMAL, channel); + } else { + Adc_Sar_Ip_DisableChannelNotifications(config->instance, + channel, ADC_SAR_IP_CHAN_NOTIF_EOC); + Adc_Sar_Ip_DisableChannel(config->instance, + ADC_SAR_IP_CONV_CHAIN_NORMAL, channel); + } + } + + /* Save ADC sequence sampling buffer and its end pointer address */ + data->buffer = sequence->buffer; + if (config->callback_select) { + data->buf_end = data->buffer + sequence->buffer_size / sizeof(uint16_t); + } + + adc_context_start_read(&data->ctx, sequence); + error = adc_context_wait_for_completion(&data->ctx); + + return error; +} + +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct adc_nxp_s32_data *data = CONTAINER_OF(ctx, struct adc_nxp_s32_data, ctx); + const struct adc_nxp_s32_config *config = data->dev->config; + + data->mask_channels = ctx->sequence.channels; + data->repeat_buffer = data->buffer; + + Adc_Sar_Ip_StartConversion(config->instance, ADC_SAR_IP_CONV_CHAIN_NORMAL); +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, + bool repeat_sampling) +{ + struct adc_nxp_s32_data *const data = + CONTAINER_OF(ctx, struct adc_nxp_s32_data, ctx); + + if (repeat_sampling) { + data->buffer = data->repeat_buffer; + } +} + +static int adc_nxp_s32_read_async(const struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + struct adc_nxp_s32_data *data = dev->data; + int error = 0; + + adc_context_lock(&data->ctx, async ? true : false, async); + error = adc_nxp_s32_start_read_async(dev, sequence); + adc_context_release(&data->ctx, error); + + return error; +} + +static int adc_nxp_s32_read(const struct device *dev, + const struct adc_sequence *sequence) +{ + return adc_nxp_s32_read_async(dev, sequence, NULL); +} + +static void adc_nxp_s32_isr(const struct device *dev) +{ + const struct adc_nxp_s32_config *config = dev->config; + + Adc_Sar_Ip_IRQHandler(config->instance); +} + +#define ADC_NXP_S32_DRIVER_API(n) \ + static const struct adc_driver_api adc_nxp_s32_driver_api_##n = { \ + .channel_setup = adc_nxp_s32_channel_setup, \ + .read = adc_nxp_s32_read, \ + IF_ENABLED(CONFIG_ADC_ASYNC, (.read_async = adc_nxp_s32_read_async,))\ + .ref_internal = DT_INST_PROP(n, vref_mv), \ + }; + +#define ADC_NXP_S32_IRQ_CONFIG(n) \ + static void adc_nxp_s32_adc_sar_config_func_##n(const struct device *dev)\ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + adc_nxp_s32_isr, DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + }; + +#define ADC_NXP_S32_CALLBACK_DEFINE(n) \ + void adc_nxp_s32_normal_end_conversion_callback##n(const uint16 PhysicalChanId)\ + { \ + const struct device *dev = DEVICE_DT_INST_GET(n); \ + const struct adc_nxp_s32_config *config = dev->config; \ + struct adc_nxp_s32_data *data = dev->data; \ + uint16_t result = 0; \ + \ + result = Adc_Sar_Ip_GetConvData(n, PhysicalChanId); \ + LOG_DBG("End conversion, channel %d, group %d, result = %d", \ + ADC_SAR_IP_CHAN_2_BIT(PhysicalChanId), \ + config->group_channel, result); \ + \ + *data->buffer++ = result; \ + data->mask_channels &= \ + ~BIT(ADC_SAR_IP_CHAN_2_BIT(PhysicalChanId)); \ + \ + if (!data->mask_channels) { \ + adc_context_on_sampling_done(&data->ctx, \ + (struct device *)dev); \ + } \ + }; \ + void adc_nxp_s32_normal_endchain_callback##n(void) \ + { \ + const struct device *dev = DEVICE_DT_INST_GET(n); \ + const struct adc_nxp_s32_config *config = dev->config; \ + struct adc_nxp_s32_data *data = dev->data; \ + uint16_t result = 0; \ + uint8_t channel; \ + \ + while (data->mask_channels) { \ + channel = ADC_NXP_S32_GROUPCHAN_2_PHYCHAN( \ + config->group_channel, \ + (find_lsb_set(data->mask_channels)-1)); \ + result = Adc_Sar_Ip_GetConvData(n, channel); \ + LOG_DBG("End chain, channel %d, group %d, result = %d", \ + ADC_SAR_IP_CHAN_2_BIT(channel), \ + config->group_channel, result); \ + if (data->buffer < data->buf_end) { \ + *data->buffer++ = result; \ + } \ + data->mask_channels &= \ + ~BIT(ADC_SAR_IP_CHAN_2_BIT(channel)); \ + } \ + \ + adc_context_on_sampling_done(&data->ctx, (struct device *)dev); \ + }; + +#define ADC_NXP_S32_INSTANCE_CHECK(indx, n) \ + ((DT_INST_REG_ADDR(n) == IP_ADC_##indx##_BASE) ? indx : 0) +#define ADC_NXP_S32_GET_INSTANCE(n) \ + LISTIFY(__DEBRACKET ADC_INSTANCE_COUNT, ADC_NXP_S32_INSTANCE_CHECK, (|), n) + +#define ADC_NXP_S32_INIT_DEVICE(n) \ + ADC_NXP_S32_DRIVER_API(n) \ + ADC_NXP_S32_CALLBACK_DEFINE(n) \ + ADC_NXP_S32_IRQ_CONFIG(n) \ + COND_CODE_1(DT_INST_NUM_PINCTRL_STATES(n), \ + (PINCTRL_DT_INST_DEFINE(n);), (EMPTY)) \ + static const Adc_Sar_Ip_ConfigType adc_nxp_s32_default_config##n = \ + { \ + .ConvMode = ADC_SAR_IP_CONV_MODE_ONESHOT, \ + .AdcResolution = ADC_SAR_IP_RESOLUTION_14, \ + .HighSpeedConvEn = DT_INST_PROP(n, high_speed), \ + .EndOfNormalChainNotification = \ + adc_nxp_s32_normal_endchain_callback##n, \ + .EndOfConvNotification = \ + adc_nxp_s32_normal_end_conversion_callback##n, \ + }; \ + static struct adc_nxp_s32_data adc_nxp_s32_data_##n = { \ + ADC_CONTEXT_INIT_TIMER(adc_nxp_s32_data_##n, ctx), \ + ADC_CONTEXT_INIT_LOCK(adc_nxp_s32_data_##n, ctx), \ + ADC_CONTEXT_INIT_SYNC(adc_nxp_s32_data_##n, ctx), \ + }; \ + static const struct adc_nxp_s32_config adc_nxp_s32_config_##n = { \ + .base = (ADC_Type *)DT_INST_REG_ADDR(n), \ + .instance = ADC_NXP_S32_GET_INSTANCE(n), \ + .group_channel = DT_INST_ENUM_IDX(n, group_channel), \ + .callback_select = DT_INST_ENUM_IDX(n, callback_select), \ + .adc_cfg = (Adc_Sar_Ip_ConfigType *)&adc_nxp_s32_default_config##n,\ + .irq_config_func = adc_nxp_s32_adc_sar_config_func_##n, \ + .pin_cfg = COND_CODE_1(DT_INST_NUM_PINCTRL_STATES(n), \ + (PINCTRL_DT_INST_DEV_CONFIG_GET(n)), (NULL)), \ + }; \ + DEVICE_DT_INST_DEFINE(n, \ + &adc_nxp_s32_init, \ + NULL, \ + &adc_nxp_s32_data_##n, \ + &adc_nxp_s32_config_##n, \ + POST_KERNEL, \ + CONFIG_ADC_INIT_PRIORITY, \ + &adc_nxp_s32_driver_api_##n); + +DT_INST_FOREACH_STATUS_OKAY(ADC_NXP_S32_INIT_DEVICE) diff --git a/dts/bindings/adc/nxp,s32-adc-sar.yaml b/dts/bindings/adc/nxp,s32-adc-sar.yaml new file mode 100644 index 00000000000..f7201698f79 --- /dev/null +++ b/dts/bindings/adc/nxp,s32-adc-sar.yaml @@ -0,0 +1,49 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: NXP S32 ADC SAR controller + +compatible: "nxp,s32-adc-sar" + +include: [adc-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + vref-mv: + type: int + default: 3300 + description: Indicates the reference voltage of the ADC in mV. + + group-channel: + type: string + required: true + enum: + - "precision" + - "standard" + - "external" + description: The ADC group channel. + + high-speed: + type: boolean + description: Use high speed during conversion, calibration. + + callback-select: + type: string + default: "normal-end-conversion" + enum: + - "normal-end-conversion" + - "normal-end-chain" + description: | + Select normal end conversion callback to reduce interrupt handling time. + Select normal end chain callback to reduce the number of interrupt occurrences. + + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input