diff --git a/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml b/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml index fa4ec97aa8afc..968d613b956a7 100644 --- a/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml +++ b/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml @@ -14,4 +14,5 @@ toolchain: - gnuarmemb supported: - gpio + - adc vendor: infineon diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index 08b8cc88bf344..6ce9b96a85e17 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -47,6 +47,7 @@ zephyr_library_sources_ifdef(CONFIG_ADC_GECKO_ADC adc_gecko.c) zephyr_library_sources_ifdef(CONFIG_ADC_GECKO_IADC iadc_gecko.c) zephyr_library_sources_ifdef(CONFIG_ADC_SILABS_SIWX91X adc_silabs_siwx91x.c) zephyr_library_sources_ifdef(CONFIG_ADC_INFINEON_CAT1 adc_ifx_cat1.c) +zephyr_library_sources_ifdef(CONFIG_ADC_INFINEON_HPPASS_SAR adc_ifx_hppass_sar.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_TLA202X adc_tla202x.c) diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index b73d9f963b7fa..a678f86b2962f 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -138,6 +138,8 @@ source "drivers/adc/Kconfig.siwx91x" source "drivers/adc/Kconfig.ifx_cat1" +source "drivers/adc/Kconfig.ifx_hppass_sar" + source "drivers/adc/Kconfig.smartbond" source "drivers/adc/Kconfig.tla202x" diff --git a/drivers/adc/Kconfig.ifx_hppass_sar b/drivers/adc/Kconfig.ifx_hppass_sar new file mode 100644 index 0000000000000..30fc8491db583 --- /dev/null +++ b/drivers/adc/Kconfig.ifx_hppass_sar @@ -0,0 +1,31 @@ +# Copyright (c) 2025 Infineon Technologies AG, +# or an affiliate of Infineon Technologies AG. +# +# SPDX-License-Identifier: Apache-2.0 + +# Infineon HPPASS SAR ADC configuration options + +config ADC_INFINEON_HPPASS_SAR + bool "Infineon HPPASS SAR ADC driver" + default y + depends on DT_HAS_INFINEON_HPPASS_SAR_ADC_ENABLED + select USE_INFINEON_HPPASS_SAR_ADC + select ADC_CONFIGURABLE_INPUTS + help + This option enables the ADC driver for Infineon PSOC C3 + HPPASS (High Performance Programmable Analog Sub-System) SAR ADC. + + The HPPASS SAR ADC provides 12-bit analog-to-digital + conversion capabilities + +if ADC_INFINEON_HPPASS_SAR + +config ADC_INFINEON_HPPASS_SAR_INIT_PRIORITY + int "Infineon HPPASS SAR ADC driver init priority" + default 80 + help + Infineon HPPASS SAR ADC driver initialization priority. + This should be higher than the HPPASS_ANALOG_INIT_PRIORITY + to ensure the analog subsystem is initialized first. + +endif # ADC_INFINEON_HPPASS_SAR diff --git a/drivers/adc/adc_ifx_hppass_sar.c b/drivers/adc/adc_ifx_hppass_sar.c new file mode 100644 index 0000000000000..b8686f655c2ef --- /dev/null +++ b/drivers/adc/adc_ifx_hppass_sar.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief ADC HPPASS SAR driver + * + * Zephyr driver file for the HPPASS SAR ADC used in the Infineon PSOC C3 series. + */ + +#define DT_DRV_COMPAT infineon_hppass_sar_adc + +#include +#include +#include +#include +#include +#include +#include + +#include "ifx_hppass_analog.h" +#include "cy_pdl.h" + +#define ADC_CONTEXT_USES_KERNEL_TIMER +#include "adc_context.h" + +#define IFX_HPPASS_SAR_ADC_RESOLUTION (12u) /* ADC Resolution for this device is fixed at 12-bit*/ + +LOG_MODULE_REGISTER(ifx_hppass_sar_adc, CONFIG_ADC_LOG_LEVEL); + +/* + * Device supports up to 28 channels. Note that channels 12-15, 16-19, 20-23, and 24-27 are + * multiplexed in hardware and share samplers. + */ +#define HPPASS_SAR_ADC_MAX_CHANNELS CY_HPPASS_SAR_CHAN_NUM +#define DIRECT_CHANNEL_CNT CY_HPPASS_SAR_DIR_SAMP_NUM +#define MUXED_CHANNELS_PER_SAMPLER 4 +#define IFX_HPPASS_SAR_SAMPLER_GAIN_MSK 0x03 +#define IFX_HPPASS_SAR_SAMPLER_GAIN_WIDTH 2 + +/******************************************************************************* + * Configuration and data structures + ******************************************************************************* + */ +struct ifx_hppass_sar_adc_config { + uint8_t irq_priority; + IRQn_Type irq_num; + void (*irq_func)(void); + uint16_t dir_samp_en_mask; + uint16_t mux_samp_en_mask; + bool vref_internal_source; + bool gain_cal; + bool offset_cal; + bool linear_cal; +}; + +/** + * @brief HPPASS SAR ADC channel configuration + */ +struct ifx_hppass_sar_adc_channel_config { + /** Channel number */ + uint8_t id; + bool enabled; + uint8_t input_positive; + /* + * PDL channel configuration structure. The PDL will reapply channel configurations for all + * channels any time a change is made to any channel configuration. Store the PDL + * configuration for this channel so we have a copy to be used for this action. + */ + cy_stc_hppass_sar_chan_t pdl_channel_cfg; +}; + +/** + * @brief HPPASS SAR ADC device data + */ +struct ifx_hppass_sar_adc_data { + /* ADC context for async operations */ + struct adc_context ctx; + const struct device *dev; + /* PDL ADC configuration structure */ + cy_stc_hppass_sar_t hppass_sar_obj; + /* channel configurations for all channels (used or not)*/ + struct ifx_hppass_sar_adc_channel_config hppass_sar_chan_obj[HPPASS_SAR_ADC_MAX_CHANNELS]; + /* Bitmask of enabled channels */ + uint32_t enabled_channels; + /* Conversion buffer */ + uint16_t *buffer; + /* Repeat buffer for continuous sampling */ + uint16_t *repeat_buffer; + /** Conversion result */ + int result; +}; + +/* + * ADC Channels 12-28 are grouped together in hardware using a mux. The grouping is as follows: + * Sampler 12: Channels 12-15, + * Sampler 13: Channels 16-19, + * Sampler 14: Channels 20-23, + * Sampler 15: Channels 24-27 + */ +#define ADC_SAMPLER_12_CHANNEL_GROUP 0x0000F000 +#define ADC_SAMPLER_13_CHANNEL_GROUP 0x000F0000 +#define ADC_SAMPLER_14_CHANNEL_GROUP 0x00F00000 +#define ADC_SAMPLER_15_CHANNEL_GROUP 0x0F000000 +#define ADC_SAMPLER_DIRECT_MASK 0x0FFF + +/** + * @brief Configure HPPASS SAR ADC group + * + * @param channels Bitmask of channels to be enabled in the group + * @param group Group number to configure (0-7) + * + * HPPASS SAR ADC has 8 groups. ADC samplers can be added to a group, and will be sampled + * simultaneously and converted sequentially when the group is triggered. Note that only one MUXed + * channel can be included in a mux group. + */ +static int ifx_hppass_sar_configure_group(uint32_t channels, uint32_t group) +{ + cy_stc_hppass_sar_grp_t group_cfg = {0}; + + /* Check that no more than one channel is selected from each muxed group */ + if (POPCOUNT(channels & ADC_SAMPLER_12_CHANNEL_GROUP) > 1 || + POPCOUNT(channels & ADC_SAMPLER_13_CHANNEL_GROUP) > 1 || + POPCOUNT(channels & ADC_SAMPLER_14_CHANNEL_GROUP) > 1 || + POPCOUNT(channels & ADC_SAMPLER_15_CHANNEL_GROUP) > 1) { + + return -EINVAL; + } + + group_cfg.trig = CY_HPPASS_SAR_TRIG_0; /* TRIG_0 used for SW Trigger */ + group_cfg.sampTime = CY_HPPASS_SAR_SAMP_TIME_DISABLED; + + /* Enable directly sampled channels. */ + group_cfg.dirSampMsk = channels & ADC_SAMPLER_DIRECT_MASK; + + /* Enable Muxed channels. We need to determine if each sampler is enabled and what the mux + * should be set to for the sampler. + */ + group_cfg.muxSampMsk = 0; + for (int channel_num = DIRECT_CHANNEL_CNT; channel_num < HPPASS_SAR_ADC_MAX_CHANNELS; + channel_num++) { + if (channels & (1 << channel_num)) { + int sampler_num = + (channel_num - DIRECT_CHANNEL_CNT) / MUXED_CHANNELS_PER_SAMPLER; + int mux_setting = + (channel_num - DIRECT_CHANNEL_CNT) % MUXED_CHANNELS_PER_SAMPLER; + group_cfg.muxSampMsk |= (1 << sampler_num); + group_cfg.muxChanIdx[sampler_num] = mux_setting; + } + } + + if (Cy_HPPASS_SAR_GroupConfig(group, &group_cfg) != 0) { + LOG_ERR("ADC Group configuration failed"); + return -EINVAL; + } + /* CrossTalkAdjust must be called any time groups are reconfigured. */ + Cy_HPPASS_SAR_CrossTalkAdjust((uint8_t)1U << group); + return 0; +} + +/** + * @brief Read results specified group of channels + * + * @param channels Bitmask of channels to read results for + * @param data Pointer to ADC data structure + * + * Helper function to read all the results for the specified channels into the data buffer. + */ +static void ifx_hppass_get_group_results(uint32_t channels, struct ifx_hppass_sar_adc_data *data) +{ + if (data->buffer == NULL) { + LOG_ERR("ADC data buffer is NULL"); + return; + } + for (size_t i = 0; i < HPPASS_SAR_ADC_MAX_CHANNELS; i++) { + if (channels & (1 << i)) { + int16_t result = Cy_HPPASS_SAR_Result_ChannelRead(i); + *data->buffer++ = result; + } + } +} + +/** + * @brief Start ADC conversion + * + * @param ctx ADC context + * + * The HPPASS SAR ADC uses a grouping functionality to simultaneously sample then convert multiple + * channels with one trigger input. All channels in the ADC Sequence are added to a group and a + * conversion is triggered. + */ +static void adc_context_start_sampling(struct adc_context *ctx) +{ + struct ifx_hppass_sar_adc_data *data = + CONTAINER_OF(ctx, struct ifx_hppass_sar_adc_data, ctx); + const struct adc_sequence *sequence = &ctx->sequence; + uint32_t result_status; + + data->repeat_buffer = data->buffer; + if (data->buffer == NULL || sequence->buffer_size == 0) { + data->result = -ENOMEM; + return; + } + if (sequence->channels == 0) { + LOG_ERR("No channels specified"); + data->result = -EINVAL; + } else if (ifx_hppass_sar_configure_group(sequence->channels, 0) != 0) { + LOG_ERR("Invalid channel group selection"); + data->result = -EINVAL; + } else { + + /* Trigger SAR ADC group 0 conversion */ + Cy_HPPASS_SAR_Result_ClearStatus(sequence->channels); + Cy_HPPASS_SetFwTrigger(CY_HPPASS_TRIG_0_MSK); + +#if defined(CONFIG_ADC_ASYNC) + if (!data->ctx.asynchronous) { +#endif /* CONFIG_ADC_ASYNC */ + /* Wait for channel conversion done */ + do { + result_status = Cy_HPPASS_SAR_Result_GetStatus(); + } while ((result_status & sequence->channels) != sequence->channels); + + ifx_hppass_get_group_results(sequence->channels, data); + adc_context_on_sampling_done(&data->ctx, data->dev); +#if defined(CONFIG_ADC_ASYNC) + } +#endif /* CONFIG_ADC_ASYNC */ + data->result = 0; + } +} + +static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) +{ + struct ifx_hppass_sar_adc_data *data = + CONTAINER_OF(ctx, struct ifx_hppass_sar_adc_data, ctx); + + if (repeat_sampling) { + data->buffer = data->repeat_buffer; + } +} + +/** + * @brief Start read operation + * + * @param dev Pointer to the device structure for the driver instance. + * @param sequence Pointer to the adc_sequence structure. + * + * This function validates the read parameters, sets up the buffer, and initiates the read + * operation using the ADC context. + */ +static int start_read(const struct device *dev, const struct adc_sequence *sequence) +{ + struct ifx_hppass_sar_adc_data *data = dev->data; + + if (sequence->buffer_size < (sizeof(int16_t) * POPCOUNT(sequence->channels))) { + LOG_ERR("Buffer too small"); + return -ENOMEM; + } + if (sequence->resolution != IFX_HPPASS_SAR_ADC_RESOLUTION) { + LOG_ERR("Unsupported resolution: %d", sequence->resolution); + return -EINVAL; + } + if (sequence->channels == 0) { + LOG_ERR("No channels specified"); + return -EINVAL; + } + if ((sequence->channels ^ (data->enabled_channels & sequence->channels)) != 0) { + LOG_ERR("Channels not configured"); + return -EINVAL; + } + + if (sequence->oversampling != 0) { + LOG_ERR("Oversampling not supported"); + return -EINVAL; + } + + data->buffer = sequence->buffer; + adc_context_start_read(&data->ctx, sequence); + + return adc_context_wait_for_completion(&data->ctx); +} + +/** + * @brief ADC interrupt handler + * + * @param dev Pointer to the device structure for the driver instance. + * + * Interrupt Handler for the combined group results interrupt. This handler is common all group + * completion interrupts. Individual group completion interrupts are available if needed for more + * advanced ADC control. + */ +static void ifx_hppass_sar_adc_isr(const struct device *dev) +{ +#if defined(CONFIG_ADC_ASYNC) + struct ifx_hppass_sar_adc_data *data = dev->data; +#else + ARG_UNUSED(dev); +#endif /* CONFIG_ADC_ASYNC */ + LOG_DBG("SAR ADC combined results interrupt"); + + /* Check which SAR result groups have completed */ + uint32_t result_intr_status = Cy_HPPASS_SAR_Result_GetInterruptStatusMasked(); + + /* Clear the specific SAR result interrupts that fired */ + Cy_HPPASS_SAR_Result_ClearInterrupt(result_intr_status); + + /* Check if Group 0 completed (which is what we're using) */ + if (result_intr_status & CY_HPPASS_INTR_SAR_RESULT_GROUP_0) { + LOG_DBG("SAR Group 0 conversion complete"); + +#if defined(CONFIG_ADC_ASYNC) + if (data->ctx.asynchronous) { + const struct adc_sequence *sequence = &data->ctx.sequence; + uint32_t result_status = Cy_HPPASS_SAR_Result_GetStatus(); + + /* Make sure all requested channels have completed */ + if ((result_status & sequence->channels) == sequence->channels) { + ifx_hppass_get_group_results(sequence->channels, data); + /* Clear the result status for the channels we read */ + Cy_HPPASS_SAR_Result_ClearStatus(result_status & + sequence->channels); + + adc_context_on_sampling_done(&data->ctx, dev); + } else { + /* + * Not all channels have completed yet. This shouldn't happen, if + * configured correctly all channels in the group will be complete + * when this interrupt occurs. + */ + LOG_ERR("SAR Group 0: Not all channels completed."); + } + } +#endif /* CONFIG_ADC_ASYNC */ + } + + /* + * This implementation only uses Group 0. Any other interrupts indicates a configuration + * error. + */ + if (result_intr_status & ~CY_HPPASS_INTR_SAR_RESULT_GROUP_0) { + LOG_ERR("SAR Results Interrupt for unhandled groups: 0x%08X", + (uint32_t)(result_intr_status & ~CY_HPPASS_INTR_SAR_RESULT_GROUP_0)); + } +} + +/** + * @brief Initialize pdl adc configuration structure + * + * This function initializes the pdl adc configuration with values derived from the device tree and + * other default values. Channel and Group configurations are set to NULL intially. Channels will + * be configured later in the channel setup function. Groups are configured when a conversion is + * started. + */ +static void _ifx_init_pdl_struct(struct ifx_hppass_sar_adc_data *data, + const struct ifx_hppass_sar_adc_config *cfg) +{ + data->hppass_sar_obj = ifx_hppass_sar_pdl_cfg_struct_default; + data->hppass_sar_obj.vref = + cfg->vref_internal_source ? CY_HPPASS_SAR_VREF_VDDA : CY_HPPASS_SAR_VREF_EXT; + data->hppass_sar_obj.offsetCal = cfg->offset_cal; + data->hppass_sar_obj.linearCal = cfg->linear_cal; + data->hppass_sar_obj.gainCal = cfg->gain_cal; + data->hppass_sar_obj.dirSampEnMsk = cfg->dir_samp_en_mask; + data->hppass_sar_obj.muxSampEnMsk = cfg->mux_samp_en_mask; +} + +/** + * @brief Initialize channel configuration structures + * + * This function initializes the channel configuration structures with default values. All + * channels are initially disabled. Channels will be enabled and configured in the channel setup + * function. + */ +static void _ifx_init_channel_cfg(struct ifx_hppass_sar_adc_data *data) +{ + for (int i = 0; i < HPPASS_SAR_ADC_MAX_CHANNELS; i++) { + data->hppass_sar_chan_obj[i] = (struct ifx_hppass_sar_adc_channel_config){ + .id = (uint8_t)i, + .input_positive = 0, + .pdl_channel_cfg = + (cy_stc_hppass_sar_chan_t){ + .diff = false, + .sign = false, + .avg = CY_HPPASS_SAR_AVG_DISABLED, + .limit = CY_HPPASS_SAR_LIMIT_DISABLED, + .result = false, + .fifo = CY_HPPASS_FIFO_DISABLED, + }, + }; + + if (data->hppass_sar_chan_obj[i].enabled) { + data->hppass_sar_obj.chan[i] = + &data->hppass_sar_chan_obj[i].pdl_channel_cfg; + } else { + data->hppass_sar_obj.chan[i] = NULL; + } + } +} + +/******************************************************************************* + * Zephyr Driver API Functions + ******************************************************************************* + */ + +/** + * @brief ADC read implementation + */ +static int ifx_hppass_sar_adc_read(const struct device *dev, const struct adc_sequence *sequence) +{ + struct ifx_hppass_sar_adc_data *data = dev->data; + int ret; + + adc_context_lock(&data->ctx, false, NULL); + ret = start_read(dev, sequence); + adc_context_release(&data->ctx, ret); + + return ret; +} + +/** + * @brief ADC read async implementation + */ +#ifdef CONFIG_ADC_ASYNC +static int ifx_hppass_sar_adc_read_async(const struct device *dev, + const struct adc_sequence *sequence, + struct k_poll_signal *async) +{ + struct ifx_hppass_sar_adc_data *data = dev->data; + int ret; + + adc_context_lock(&data->ctx, true, async); + ret = start_read(dev, sequence); + adc_context_release(&data->ctx, ret); + return ret; +} +#endif + +/** + * @brief Configure ADC channel + * + * Implements the Zephyr ADC channel configuration API. + */ +static int ifx_hppass_sar_adc_channel_setup(const struct device *dev, + const struct adc_channel_cfg *channel_cfg) +{ + struct ifx_hppass_sar_adc_data *data = dev->data; + + if (channel_cfg->channel_id >= HPPASS_SAR_ADC_MAX_CHANNELS) { + LOG_ERR("Invalid channel ID: %d", channel_cfg->channel_id); + return -EINVAL; + } + + if (channel_cfg->differential) { + LOG_ERR("Differential channels not supported"); + return -ENOTSUP; + } + + if (channel_cfg->gain != ADC_GAIN_1 && channel_cfg->gain != ADC_GAIN_3 && + channel_cfg->gain != ADC_GAIN_6 && channel_cfg->gain != ADC_GAIN_12) { + LOG_ERR("Gain setting not supported"); + return -EINVAL; + } + + /* + * The HPPASS SAR hardware block does not support setting the reference individually per + * channel. The device can select internal or external reference that will apply to all ADC + * channels. + */ + if (channel_cfg->reference != ADC_REF_INTERNAL && + channel_cfg->reference != ADC_REF_EXTERNAL0) { + LOG_ERR("Reference setting not supported"); + return -EINVAL; + } + + /* + * The HPPASS SAR Hardware block does not support setting acquisition time per channel. The + * device has three sampling time configuration registers. These registers are used to + * configure the sample time for a group rather than individual channels. + */ + if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { + LOG_ERR("Invalid channel acquisition time, expected ADC_ACQ_TIME_DEFAULT"); + return -EINVAL; + } + + data->hppass_sar_chan_obj[channel_cfg->channel_id].id = channel_cfg->channel_id; + data->hppass_sar_chan_obj[channel_cfg->channel_id].pdl_channel_cfg = + (cy_stc_hppass_sar_chan_t){ + .diff = (channel_cfg->differential == 0) ? false : true, + .sign = false, + .avg = CY_HPPASS_SAR_AVG_DISABLED, + .limit = CY_HPPASS_SAR_LIMIT_DISABLED, + .result = true, + .fifo = CY_HPPASS_FIFO_DISABLED, + }; + + data->hppass_sar_obj.chan[channel_cfg->channel_id] = + &data->hppass_sar_chan_obj[channel_cfg->channel_id].pdl_channel_cfg; + + if (Cy_HPPASS_SAR_ChannelConfig( + channel_cfg->channel_id, + &data->hppass_sar_chan_obj[channel_cfg->channel_id].pdl_channel_cfg) != + CY_HPPASS_SUCCESS) { + LOG_ERR("Channel %d configuration failed", channel_cfg->channel_id); + return -EIO; + } + + /* + * PDL doesn't support configuring gain except during device initialization. Initialize the + * gain registers directly here. + */ + uint32_t sampler_gain; + + HPPASS_SAR_SAMP_GAIN(HPPASS_BASE) &= + ~(IFX_HPPASS_SAR_SAMPLER_GAIN_MSK + << (channel_cfg->channel_id * IFX_HPPASS_SAR_SAMPLER_GAIN_WIDTH)); + switch (channel_cfg->gain) { + case ADC_GAIN_1: + sampler_gain = 0; + break; + case ADC_GAIN_3: + sampler_gain = 1; + break; + case ADC_GAIN_6: + sampler_gain = 2; + break; + case ADC_GAIN_12: + sampler_gain = 3; + break; + default: + sampler_gain = 0; + break; + } + HPPASS_SAR_SAMP_GAIN(HPPASS_BASE) |= + (sampler_gain << (channel_cfg->channel_id * IFX_HPPASS_SAR_SAMPLER_GAIN_WIDTH)); + + data->enabled_channels |= BIT(channel_cfg->channel_id); + + return 0; +} + +/** + * @brief Initialize ADC device + */ +static int ifx_hppass_sar_adc_init(const struct device *dev) +{ + const struct ifx_hppass_sar_adc_config *cfg = dev->config; + struct ifx_hppass_sar_adc_data *data = dev->data; + + LOG_DBG("Initializing HPPASS SAR ADC"); + + /* + * Initialize the data structure. The data structure contains a pdl device initialization + * object which we store to be able to reinitialize the ADC if needed. + */ + _ifx_init_pdl_struct(data, cfg); + _ifx_init_channel_cfg(data); + + if (Cy_HPPASS_SAR_Init(&data->hppass_sar_obj) != CY_RSLT_SUCCESS) { + LOG_ERR("Failed to initialize HPPASS SAR ADC"); + return -EIO; + } + if (ifx_hppass_ac_init_adc() != CY_RSLT_SUCCESS) { + LOG_ERR("HPPASS AC failed to initialize ADC"); + return -EIO; + } + +#if defined(CONFIG_ADC_ASYNC) + Cy_HPPASS_SAR_Result_SetInterruptMask(CY_HPPASS_INTR_SAR_RESULT_GROUP_0); + cfg->irq_func(); +#endif /* CONFIG_ADC_ASYNC */ + + adc_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +#ifdef CONFIG_ADC_ASYNC +#define ADC_IFX_HPPASS_SAR_DRIVER_API(n) \ + static DEVICE_API(adc, adc_ifx_hppass_sar_driver_api_##n) = { \ + .channel_setup = ifx_hppass_sar_adc_channel_setup, \ + .read = ifx_hppass_sar_adc_read, \ + .read_async = ifx_hppass_sar_adc_read_async, \ + .ref_internal = DT_INST_PROP(n, vref_mv), \ + }; +#else +#define ADC_IFX_HPPASS_SAR_DRIVER_API(n) \ + static DEVICE_API(adc, adc_ifx_hppass_sar_driver_api_##n) = { \ + .channel_setup = ifx_hppass_sar_adc_channel_setup, \ + .read = ifx_hppass_sar_adc_read, \ + .ref_internal = DT_INST_PROP(n, vref_mv), \ + }; +#endif + +/* + * ===================================================================== + * Devicetree channel mask generation + * + * dir_samp_en_mask: + * One bit per direct sampler channel (0..11) that has a child node. + * + * mux_samp_en_mask: + * One bit per mux sampler group: + * Bit0 -> any of channels 12..15 present + * Bit1 -> any of channels 16..19 present + * Bit2 -> any of channels 20..23 present + * Bit3 -> any of channels 24..27 present + * ===================================================================== + */ + +#define IFX_HPPASS_SAR_CH_EXISTS(inst, ch) DT_NODE_EXISTS(DT_CHILD(DT_DRV_INST(inst), channel_##ch)) + +/* Direct sampler bitmap (0..11) */ +#define IFX_HPPASS_SAR_DIR_MASK(inst) \ + ((IFX_HPPASS_SAR_CH_EXISTS(inst, 0) ? BIT(0) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 1) ? BIT(1) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 2) ? BIT(2) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 3) ? BIT(3) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 4) ? BIT(4) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 5) ? BIT(5) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 6) ? BIT(6) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 7) ? BIT(7) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 8) ? BIT(8) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 9) ? BIT(9) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 10) ? BIT(10) : 0) | \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 11) ? BIT(11) : 0)) + +/* Group presence helpers */ +#define IFX_HPPASS_SAR_GRP0_PRESENT(inst) \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 12) || IFX_HPPASS_SAR_CH_EXISTS(inst, 13) || \ + IFX_HPPASS_SAR_CH_EXISTS(inst, 14) || IFX_HPPASS_SAR_CH_EXISTS(inst, 15)) + +#define IFX_HPPASS_SAR_GRP1_PRESENT(inst) \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 16) || IFX_HPPASS_SAR_CH_EXISTS(inst, 17) || \ + IFX_HPPASS_SAR_CH_EXISTS(inst, 18) || IFX_HPPASS_SAR_CH_EXISTS(inst, 19)) + +#define IFX_HPPASS_SAR_GRP2_PRESENT(inst) \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 20) || IFX_HPPASS_SAR_CH_EXISTS(inst, 21) || \ + IFX_HPPASS_SAR_CH_EXISTS(inst, 22) || IFX_HPPASS_SAR_CH_EXISTS(inst, 23)) + +#define IFX_HPPASS_SAR_GRP3_PRESENT(inst) \ + (IFX_HPPASS_SAR_CH_EXISTS(inst, 24) || IFX_HPPASS_SAR_CH_EXISTS(inst, 25) || \ + IFX_HPPASS_SAR_CH_EXISTS(inst, 26) || IFX_HPPASS_SAR_CH_EXISTS(inst, 27)) + +/* Mux sampler enable mask (bit per group if any channel in that group exists) */ +#define IFX_HPPASS_SAR_MUX_MASK(inst) \ + ((IFX_HPPASS_SAR_GRP0_PRESENT(inst) ? BIT(0) : 0) | \ + (IFX_HPPASS_SAR_GRP1_PRESENT(inst) ? BIT(1) : 0) | \ + (IFX_HPPASS_SAR_GRP2_PRESENT(inst) ? BIT(2) : 0) | \ + (IFX_HPPASS_SAR_GRP3_PRESENT(inst) ? BIT(3) : 0)) + +/* Device instantiation */ +#define IFX_HPPASS_SAR_ADC_INIT(n) \ + ADC_IFX_HPPASS_SAR_DRIVER_API(n); \ + static void ifx_hppass_sar_adc_config_func_##n(void); \ + static const struct ifx_hppass_sar_adc_config ifx_hppass_sar_adc_config_##n = { \ + .irq_priority = DT_INST_IRQ(n, priority), \ + .irq_num = DT_INST_IRQN(n), \ + .irq_func = ifx_hppass_sar_adc_config_func_##n, \ + .dir_samp_en_mask = IFX_HPPASS_SAR_DIR_MASK(n), \ + .mux_samp_en_mask = IFX_HPPASS_SAR_MUX_MASK(n), \ + .vref_internal_source = DT_INST_PROP(n, ref_internal_source), \ + .gain_cal = DT_INST_PROP(n, gain_cal), \ + .offset_cal = DT_INST_PROP(n, offset_cal), \ + .linear_cal = DT_INST_PROP(n, linear_cal)}; \ + static struct ifx_hppass_sar_adc_data ifx_hppass_sar_adc_data_##n = { \ + ADC_CONTEXT_INIT_TIMER(ifx_hppass_sar_adc_data_##n, ctx), \ + ADC_CONTEXT_INIT_LOCK(ifx_hppass_sar_adc_data_##n, ctx), \ + ADC_CONTEXT_INIT_SYNC(ifx_hppass_sar_adc_data_##n, ctx), \ + }; \ + DEVICE_DT_INST_DEFINE(n, &ifx_hppass_sar_adc_init, NULL, &ifx_hppass_sar_adc_data_##n, \ + &ifx_hppass_sar_adc_config_##n, POST_KERNEL, \ + CONFIG_ADC_INFINEON_HPPASS_SAR_INIT_PRIORITY, \ + &adc_ifx_hppass_sar_driver_api_##n); \ + static void ifx_hppass_sar_adc_config_func_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ifx_hppass_sar_adc_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } + +DT_INST_FOREACH_STATUS_OKAY(IFX_HPPASS_SAR_ADC_INIT) diff --git a/dts/arm/infineon/cat1b/psc3/psc3.dtsi b/dts/arm/infineon/cat1b/psc3/psc3.dtsi index 4fd0e445ba677..2e86661e07835 100644 --- a/dts/arm/infineon/cat1b/psc3/psc3.dtsi +++ b/dts/arm/infineon/cat1b/psc3/psc3.dtsi @@ -128,12 +128,20 @@ #gpio-cells = <2>; }; - adc0: adc@42b70000 { - compatible = "infineon,adc-hppass-saradc"; - reg = <0x42b70000 0x10000>; - interrupts = <109 4>; - status = "disabled"; - #io-channel-cells = <1>; + hppass_analog0: hppass-analog@42b00000 { + reg = <0x42b00000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + adc0: adc@70000 { + compatible = "infineon,hppass-sar-adc"; + /* Offset within HPPASS analog region => 0x42b70000 physical */ + reg = <0x00070000 0x10000>; + interrupts = <109 4>; + status = "disabled"; + #io-channel-cells = <1>; + }; }; ipc0: ipc@421d0000 { diff --git a/dts/arm/infineon/cat1b/psc3/psc3_s.dtsi b/dts/arm/infineon/cat1b/psc3/psc3_s.dtsi index 22ad1853b7c48..f7d652cf8ad81 100644 --- a/dts/arm/infineon/cat1b/psc3/psc3_s.dtsi +++ b/dts/arm/infineon/cat1b/psc3/psc3_s.dtsi @@ -128,12 +128,20 @@ #gpio-cells = <2>; }; - adc0: adc@52b70000 { - compatible = "infineon,adc-hppass-saradc"; - reg = <0x52b70000 0x10000>; - interrupts = <109 4>; - status = "disabled"; - #io-channel-cells = <1>; + hppass_analog0: hppass-analog@52b00000 { + reg = <0x52b00000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + adc0: adc@70000 { + compatible = "infineon,hppass-sar-adc"; + /* Offset within HPPASS analog region */ + reg = <0x00070000 0x10000>; + interrupts = <109 4>; + status = "disabled"; + #io-channel-cells = <1>; + }; }; ipc0: ipc@521d0000 { diff --git a/dts/bindings/adc/infineon,hppass-sar-adc.yaml b/dts/bindings/adc/infineon,hppass-sar-adc.yaml new file mode 100644 index 0000000000000..06e28588ab519 --- /dev/null +++ b/dts/bindings/adc/infineon,hppass-sar-adc.yaml @@ -0,0 +1,75 @@ +# Copyright (c) 2025 Infineon Technologies AG, +# or an affiliate of Infineon Technologies AG. +# +# SPDX-License-Identifier: Apache-2.0 + +description: | + Infineon PSOC C3 HPPASS SAR ADC + + The HPPASS (High Performance Programmable Analog Sub-System) SAR ADC + provides high-resolution analog-to-digital conversion capabilities + for the PSOC C3 family of microcontrollers. + + Each ADC channel corresponds to a dedicated analog input pin, except for + the last four sampler inputs which are muxed. See the device datasheet for + pin assignments and mux options. + + Dependency: This ADC node must be a child of the HPPASS analog subsystem + node ("infineon,hppass-analog"), which provides clock, power, and reference + resources. + +compatible: "infineon,hppass-sar-adc" + +include: adc-controller.yaml + +properties: + reg: + required: true + description: Base address of the HPPASS SAR ADC registers + + interrupts: + required: true + description: Interrupt configuration for the HPPASS SAR ADC + + "#io-channel-cells": + const: 1 + description: Number of cells in an io-channel specifier + + clock-frequency: + type: int + description: | + ADC clock frequency in Hz. If not specified, the driver will use + the default clock configuration. + + vref-mv: + type: int + default: 3300 + description: | + Internal reference voltage in millivolts. + + offset-cal: + type: boolean + description: + Enables Self-Calibration for offset correction within the ADC. If left disabled, + the factory calibration for offset correction will be used. + + gain-cal: + type: boolean + description: | + Enables Self-Calibration for gain within the ADC. If left disabled, + the factory calibration for gain will be used. + + linear-cal: + type: boolean + description: | + Enables Self-Calibration for linearity correction within the ADC. If left disabled, + the factory calibration for linearity will be used. + + ref-internal-source: + type: boolean + description: | + Selects whether the ADC uses internal (true) or external (false) reference. + External reference recommended for best performance. + +io-channel-cells: + - input diff --git a/modules/hal_infineon/CMakeLists.txt b/modules/hal_infineon/CMakeLists.txt index 09cb72b0d8a3c..aee76b99ec0b2 100644 --- a/modules/hal_infineon/CMakeLists.txt +++ b/modules/hal_infineon/CMakeLists.txt @@ -39,6 +39,10 @@ if (CONFIG_SOC_FAMILY_INFINEON_CAT1 AND NOT CONFIG_SOC_FAMILY_PSOC6_LEGACY) endif() +if(CONFIG_SOC_SERIES_PSC3) + add_subdirectory(zephyr-ifx-cycfg) +endif() + ## Add Wi-Fi assets for AIROC devices if (CONFIG_WIFI_AIROC) add_subdirectory(wifi-host-driver) diff --git a/modules/hal_infineon/Kconfig b/modules/hal_infineon/Kconfig index eba1a49e24e73..6b0920b705e72 100644 --- a/modules/hal_infineon/Kconfig +++ b/modules/hal_infineon/Kconfig @@ -14,10 +14,20 @@ config USE_INFINEON_ADC help Enable Analog-to-Digital Converter (ADC) HAL module driver for Infineon devices +config USE_INFINEON_HPPASS_SAR_ADC + bool + help + Enable Infineon HPPASS SAR ADC PDL library support + +config USE_INFINEON_HPPASS_ANALOG + bool + help + Enable Infineon HPPASS Analog PDL library support + config USE_INFINEON_DMA bool help - Enable ADC HAL module driver for Infineon devices + Enable DMA HAL module driver for Infineon devices config USE_INFINEON_I2C bool diff --git a/modules/hal_infineon/mtb-pdl-cat1/CMakeLists.txt b/modules/hal_infineon/mtb-pdl-cat1/CMakeLists.txt index 2e92cc1e24021..f8b02df91d2b5 100644 --- a/modules/hal_infineon/mtb-pdl-cat1/CMakeLists.txt +++ b/modules/hal_infineon/mtb-pdl-cat1/CMakeLists.txt @@ -56,6 +56,11 @@ else() zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_ADC ${pdl_drv_dir}/source/cy_sar.c) endif() +zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_HPPASS_SAR_ADC ${pdl_drv_dir}/source/cy_hppass_sar.c) +zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_HPPASS_SAR_ADC ${pdl_drv_dir}/source/cy_hppass.c) +zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_HPPASS_SAR_ADC ${pdl_drv_dir}/source/cy_hppass_csg.c) +zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_HPPASS_SAR_ADC ${pdl_drv_dir}/source/cy_hppass_ac.c) + if(CONFIG_USE_INFINEON_TRNG) zephyr_library_sources(${pdl_drv_dir}/source/cy_crypto.c) zephyr_library_sources(${pdl_drv_dir}/source/cy_crypto_core_trng_v1.c) diff --git a/modules/hal_infineon/zephyr-ifx-cycfg/CMakeLists.txt b/modules/hal_infineon/zephyr-ifx-cycfg/CMakeLists.txt new file mode 100644 index 0000000000000..5c556f67cb039 --- /dev/null +++ b/modules/hal_infineon/zephyr-ifx-cycfg/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Infineon Technologies AG, +# or an affiliate of Infineon Technologies AG. +# +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_SOC_SERIES_PSC3) + set(zephyr_ifx_cycfg_dir ${ZEPHYR_HAL_INFINEON_MODULE_DIR}/zephyr-ifx-cycfg/soc_psc3) + + zephyr_include_directories(${zephyr_ifx_cycfg_dir}) + zephyr_library_sources_ifdef(CONFIG_USE_INFINEON_HPPASS_SAR_ADC ${zephyr_ifx_cycfg_dir}/ifx_hppass_analog.c) + zephyr_library_sources(${zephyr_ifx_cycfg_dir}/ifx_cycfg_init.c) +endif() diff --git a/samples/drivers/adc/adc_dt/boards/kit_psc3m5_evk.overlay b/samples/drivers/adc/adc_dt/boards/kit_psc3m5_evk.overlay new file mode 100644 index 0000000000000..98056e91dbcbc --- /dev/null +++ b/samples/drivers/adc/adc_dt/boards/kit_psc3m5_evk.overlay @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + zephyr,user { + io-channels = <&adc0 1>, <&adc0 2>, <&adc0 12>; + }; +}; + + +&hppass_analog0 { + status = "okay"; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; /* child channels */ + #size-cells = <0>; + + channel@1 { + reg = <1>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; + + channel@2 { + reg = <2>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; + + channel@12 { + reg = <12>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; +}; diff --git a/samples/drivers/adc/adc_sequence/boards/kit_psc3m5_evk.overlay b/samples/drivers/adc/adc_sequence/boards/kit_psc3m5_evk.overlay new file mode 100644 index 0000000000000..4898af9e58972 --- /dev/null +++ b/samples/drivers/adc/adc_sequence/boards/kit_psc3m5_evk.overlay @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + aliases { + adc0 = &adc0; + }; +}; + + +&hppass_analog0 { + status = "okay"; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; /* child channels */ + #size-cells = <0>; + + channel@1 { + reg = <1>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; + + channel@2 { + reg = <2>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; + + channel@12 { + reg = <12>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; +}; diff --git a/soc/infineon/cat1b/psc3/soc.c b/soc/infineon/cat1b/psc3/soc.c index 3676eb0d6b02c..aa0fea00d8018 100644 --- a/soc/infineon/cat1b/psc3/soc.c +++ b/soc/infineon/cat1b/psc3/soc.c @@ -5,16 +5,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include - -#include -#include -#include "cy_pdl.h" +#include void soc_early_init_hook(void) { - /* Initializes the system */ - SystemInit(); + ifx_cycfg_init(); } diff --git a/tests/drivers/adc/adc_api/boards/kit_psc3m5_evk.overlay b/tests/drivers/adc/adc_api/boards/kit_psc3m5_evk.overlay new file mode 100644 index 0000000000000..b6ebb4d39cdc1 --- /dev/null +++ b/tests/drivers/adc/adc_api/boards/kit_psc3m5_evk.overlay @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + zephyr,user { + io-channels = <&adc0 1>, <&adc0 2>, <&adc0 12>; + }; +}; + + +&hppass_analog0 { + status = "okay"; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; /* child channels */ + #size-cells = <0>; + + channel@1 { + reg = <1>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_EXTERNAL0"; + zephyr,resolution = <12>; + }; + + channel@2 { + reg = <2>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_EXTERNAL0"; + zephyr,resolution = <12>; + }; + + channel@12 { + reg = <12>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_EXTERNAL0"; + zephyr,resolution = <12>; + }; +}; diff --git a/tests/drivers/adc/adc_error_cases/boards/kit_psc3m5_evk.overlay b/tests/drivers/adc/adc_error_cases/boards/kit_psc3m5_evk.overlay new file mode 100644 index 0000000000000..a1e5fa9f8cebf --- /dev/null +++ b/tests/drivers/adc/adc_error_cases/boards/kit_psc3m5_evk.overlay @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + aliases { + adc = &adc0; + }; +}; + + +&hppass_analog0 { + status = "okay"; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; /* child channels */ + #size-cells = <0>; + + channel@1 { + reg = <1>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; + + channel@2 { + reg = <2>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; + + channel@12 { + reg = <12>; + zephyr,acquisition-time = ; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,resolution = <12>; + }; +}; diff --git a/tests/drivers/adc/adc_error_cases/src/adc_error_cases.c b/tests/drivers/adc/adc_error_cases/src/adc_error_cases.c index 754d2ad98d7bf..2ea1bc33b2052 100644 --- a/tests/drivers/adc/adc_error_cases/src/adc_error_cases.c +++ b/tests/drivers/adc/adc_error_cases/src/adc_error_cases.c @@ -24,7 +24,7 @@ static const struct adc_channel_cfg valid_channel_cfg = { #endif }; -#if defined(CONFIG_SOC_FAMILY_SILABS_S2) +#if defined(CONFIG_SOC_FAMILY_SILABS_S2) || defined(CONFIG_SOC_SERIES_PSC3) #define VALID_RESOLUTION 12 #else #define VALID_RESOLUTION 10 diff --git a/west.yml b/west.yml index 263cb9afa2889..762e1ad67f61e 100644 --- a/west.yml +++ b/west.yml @@ -185,7 +185,7 @@ manifest: groups: - hal - name: hal_infineon - revision: f78b8f8202db0115dc41aedda6f77dee8985254f + revision: pull/32/head path: modules/hal/infineon groups: - hal