Skip to content

Imx93 sar adc #92882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion boards/nxp/imx93_evk/imx93_evk_mimx9352_m33.dts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 NXP
* Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -69,6 +69,10 @@
pinctrl-names = "default";
};

&sar_adc1 {
status = "okay";
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update imx93_evk_mimx9352_m33.yaml to add adc in supported features?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

&gpio1 {
status = "okay";
};
Expand Down
3 changes: 2 additions & 1 deletion boards/nxp/imx93_evk/imx93_evk_mimx9352_m33.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2024 NXP
# Copyright 2024-2025 NXP
# SPDX-License-Identifier: Apache-2.0

identifier: imx93_evk/mimx9352/m33
Expand All @@ -13,4 +13,5 @@ flash: 128
supported:
- gpio
- uart
- adc
vendor: nxp
1 change: 1 addition & 0 deletions drivers/adc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC12 adc_mcux_adc12.c)
zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC16 adc_mcux_adc16.c)
zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_12B1MSPS_SAR adc_mcux_12b1msps_sar.c)
zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_LPADC adc_mcux_lpadc.c)
zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_SAR_ADC adc_mcux_sar_adc.c)
zephyr_library_sources_ifdef(CONFIG_ADC_VF610 adc_vf610.c)
zephyr_library_sources_ifdef(CONFIG_ADC_SAM_AFEC adc_sam_afec.c)
zephyr_library_sources_ifdef(CONFIG_ADC_NRFX_ADC adc_nrfx_adc.c)
Expand Down
23 changes: 23 additions & 0 deletions drivers/adc/Kconfig.mcux
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ config ADC_MCUX_LPADC
help
Enable the MCUX LPADC driver.

config ADC_MCUX_SAR_ADC
bool "MCUX SAR ADC driver"
default y
select ADC_CONFIGURABLE_INPUTS
depends on DT_HAS_NXP_SAR_ADC_ENABLED
help
Enable the MCUX SAR ADC driver.

if ADC_MCUX_12B1MSPS_SAR || ADC_MCUX_LPADC
config ADC_MCUX_ETC
bool "MCUX ADC ETC driver"
Expand Down Expand Up @@ -123,3 +131,18 @@ config LPADC_CHANNEL_COUNT


endif # ADC_MCUX_LPADC

if ADC_MCUX_SAR_ADC

config SAR_ADC_CHANNEL_COUNT
int "SAR_ADC channel count"
default 8
range 1 8
help
Amount of hardware command channels to use, reduce to save RAM.
The user can reduce this value if their application uses fewer than
8 ADC channels. This value corresponds to how many of the CMD
registers can be configured within the ADC.


endif # ADC_MCUX_SAR_ADC
231 changes: 231 additions & 0 deletions drivers/adc/adc_mcux_sar_adc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
* Copyright 2025 NXP
*
* Based on adc_mcux_sar_adc.c, which is:
* Copyright 2023-2024 NXP
* Copyright (c) 2020 Toby Firth
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT nxp_sar_adc

#include <errno.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/pinctrl.h>

#define LOG_LEVEL CONFIG_ADC_LOG_LEVEL
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
#include <fsl_sar_adc.h>
LOG_MODULE_REGISTER(adc_mcux_sar_adc);

#define ADC_CONTEXT_USES_KERNEL_TIMER
#include "adc_context.h"

struct mcux_sar_adc_config {
ADC_Type *base;
void (*irq_config_func)(const struct device *dev);
const struct device *clock_dev;
clock_control_subsys_t clock_subsys;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need clock_dev and clock_subsys in the current driver?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need clock_dev and clock_subsys in the current driver?

We didn't use clock_dev and clock_subsys now, but it is better to keep it.

};

struct mcux_sar_adc_data {
const struct device *dev;
struct adc_context ctx;
uint16_t *buffer;
uint16_t *repeat_buffer;
uint32_t channels;
};

static int mcux_sar_adc_channel_setup(const struct device *dev,
const struct adc_channel_cfg *channel_cfg)
{
/* User may configure maximum number of active channels */
if (channel_cfg->channel_id >= CONFIG_SAR_ADC_CHANNEL_COUNT) {
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->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 mcux_sar_adc_start_read(const struct device *dev, const struct adc_sequence *sequence)
{
const struct mcux_sar_adc_config *config = dev->config;
struct mcux_sar_adc_data *data = dev->data;
ADC_Type *base = config->base;
uint8_t channel_id;

if (sequence->resolution != 12) {
LOG_ERR("Unsupported resolution %d", sequence->resolution);
return -ENOTSUP;
}

channel_id = CONFIG_SAR_ADC_CHANNEL_COUNT;
while (channel_id-- > 0) {
if (sequence->channels & BIT(channel_id)) {
ADC_EnableSpecificChannelNormalConv(base, channel_id);
} else {
ADC_DisableSpecificChannelNormalConv(base, channel_id);
}
}

data->buffer = sequence->buffer;

adc_context_start_read(&data->ctx, sequence);
int error = adc_context_wait_for_completion(&data->ctx);

return error;
}

static int mcux_sar_adc_read_async(const struct device *dev, const struct adc_sequence *sequence,
struct k_poll_signal *async)
{
struct mcux_sar_adc_data *data = dev->data;
int error;

adc_context_lock(&data->ctx, async ? true : false, async);
error = mcux_sar_adc_start_read(dev, sequence);
adc_context_release(&data->ctx, error);

return error;
}

static int mcux_sar_adc_read(const struct device *dev, const struct adc_sequence *sequence)
{
return mcux_sar_adc_read_async(dev, sequence, NULL);
}

static void adc_context_start_sampling(struct adc_context *ctx)
{
struct mcux_sar_adc_data *data = CONTAINER_OF(ctx, struct mcux_sar_adc_data, ctx);
const struct mcux_sar_adc_config *config = data->dev->config;
ADC_Type *base = config->base;

data->channels = ctx->sequence.channels;
data->repeat_buffer = data->buffer;

ADC_StartConvChain(base, kADC_NormalConvOneShotMode);
}

static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling)
{
struct mcux_sar_adc_data *data = CONTAINER_OF(ctx, struct mcux_sar_adc_data, ctx);

if (repeat_sampling) {
data->buffer = data->repeat_buffer;
}
}

static void mcux_sar_adc_isr(const struct device *dev)
{
const struct mcux_sar_adc_config *config = dev->config;
struct mcux_sar_adc_data *data = dev->data;
ADC_Type *base = config->base;
adc_conv_result_t conv_result;
uint16_t channel_id;

if (((ADC_GetConvIntStatus(base) & kADC_NormalConvChainEndIntFlag))) {
ADC_ClearConvIntStatus(base, kADC_NormalConvChainEndIntFlag);
}

for (channel_id = 0; channel_id < CONFIG_SAR_ADC_CHANNEL_COUNT; channel_id++) {
if (ADC_GetChannelConvResult(base, &conv_result, channel_id)) {
data->channels &= ~BIT(channel_id);
*(data->buffer++) = conv_result.convData;
if (data->channels == 0) {
adc_context_on_sampling_done(&data->ctx, dev);
}
}
}
}

static int mcux_sar_adc_init(const struct device *dev)
{
const struct mcux_sar_adc_config *config = dev->config;
struct mcux_sar_adc_data *data = dev->data;
ADC_Type *base = config->base;
adc_config_t adc_config;
adc_calibration_config_t calibrationConfig;

ADC_GetDefaultConfig(&adc_config);
ADC_Init(base, &adc_config);
ADC_SetConvMode(base, kADC_NormalConvOneShotMode);
ADC_EnableConvInt(base, (uint32_t)kADC_NormalConvChainEndIntEnable);

/* Do calibration to reduce or eliminate the various error contribution effects. */
calibrationConfig.enableAverage = true;
calibrationConfig.sampleTime = kADC_SampleTime22;
#if (defined(FSL_FEATURE_ADC_HAS_CALBISTREG) && (FSL_FEATURE_ADC_HAS_CALBISTREG == 1U))
calibrationConfig.averageSampleNumbers = kADC_AverageSampleNumbers32;
#else
calibrationConfig.averageSampleNumbers = kADC_AverageSampleNumbers512;
#endif /* FSL_FEATURE_ADC_HAS_CALBISTREG */

if (!(ADC_DoCalibration(base, &calibrationConfig))) {
LOG_WRN("Calibration failed.");
}

config->irq_config_func(dev);
data->dev = dev;

adc_context_unlock_unconditionally(&data->ctx);

return 0;
}

static DEVICE_API(adc, mcux_sar_adc_driver_api) = {
.channel_setup = mcux_sar_adc_channel_setup,
.read = mcux_sar_adc_read,
#ifdef CONFIG_ADC_ASYNC
.read_async = mcux_sar_adc_read_async,
#endif
};

#define SAR_ADC_MCUX_INIT(n) \
\
static void mcux_sar_adc_config_func_##n(const struct device *dev); \
\
static const struct mcux_sar_adc_config mcux_sar_adc_config_##n = { \
.base = (ADC_Type *)DT_INST_REG_ADDR(n), \
.irq_config_func = mcux_sar_adc_config_func_##n, \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \
}; \
static struct mcux_sar_adc_data mcux_sar_adc_data_##n = { \
ADC_CONTEXT_INIT_TIMER(mcux_sar_adc_data_##n, ctx), \
ADC_CONTEXT_INIT_LOCK(mcux_sar_adc_data_##n, ctx), \
ADC_CONTEXT_INIT_SYNC(mcux_sar_adc_data_##n, ctx), \
}; \
\
DEVICE_DT_INST_DEFINE(n, &mcux_sar_adc_init, NULL, &mcux_sar_adc_data_##n, \
&mcux_sar_adc_config_##n, POST_KERNEL, CONFIG_ADC_INIT_PRIORITY, \
&mcux_sar_adc_driver_api); \
\
static void mcux_sar_adc_config_func_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mcux_sar_adc_isr, \
DEVICE_DT_INST_GET(n), 0); \
\
irq_enable(DT_INST_IRQN(n)); \
}

DT_INST_FOREACH_STATUS_OKAY(SAR_ADC_MCUX_INIT)
6 changes: 6 additions & 0 deletions drivers/clock_control/clock_control_mcux_ccm_rev2.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ static int mcux_ccm_get_subsys_rate(const struct device *dev,
break;
#endif

#ifdef CONFIG_ADC_MCUX_SAR_ADC
case IMX_CCM_SAR_ADC1_CLK:
clock_root = kCLOCK_Root_Adc + instance;
break;
#endif

#if defined(CONFIG_ETH_NXP_IMX_NETC)
case IMX_CCM_NETC_CLK:
clock_root = kCLOCK_Root_Netc;
Expand Down
11 changes: 10 additions & 1 deletion dts/arm/nxp/nxp_imx93_m33.dtsi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 NXP
* Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -95,6 +95,15 @@
clocks = <&ccm IMX_CCM_LPUART2_CLK 0x6c 24>;
status = "disabled";
};

sar_adc1: adc@44530000 {
compatible = "nxp,sar-adc";
reg = <0x44530000 DT_SIZE_K(64)>;
interrupts = <219 0>;
status = "disabled";
#io-channel-cells = <1>;
clocks = <&ccm IMX_CCM_SAR_ADC1_CLK 0x0 0>;
};
};
};

Expand Down
21 changes: 21 additions & 0 deletions dts/bindings/adc/nxp,sar-adc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

description: NXP SAR ADC controller

compatible: "nxp,sar-adc"

include: [adc-controller.yaml]

properties:
reg:
required: true

interrupts:
required: true

"#io-channel-cells":
const: 1

io-channel-cells:
- input
3 changes: 2 additions & 1 deletion include/zephyr/dt-bindings/clock/imx_ccm_rev2.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021,2024 NXP
* Copyright 2021,2024-2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -121,6 +121,7 @@
/* ADC */
#define IMX_CCM_LPADC1_CLK 0x1500UL
#define IMX_CCM_LPADC2_CLK 0x1501UL
#define IMX_CCM_SAR_ADC1_CLK 0x1500UL

/* TPM */
#define IMX_CCM_TPM_CLK 0x1600UL
Expand Down
1 change: 1 addition & 0 deletions modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ endif()
set_variable_ifdef(CONFIG_HWINFO_MCUX_SRC_V2 CONFIG_MCUX_COMPONENT_driver.src_2)
set_variable_ifdef(CONFIG_GPIO_MCUX_IGPIO CONFIG_MCUX_COMPONENT_driver.igpio)
set_variable_ifdef(CONFIG_ADC_MCUX_LPADC CONFIG_MCUX_COMPONENT_driver.lpadc)
set_variable_ifdef(CONFIG_ADC_MCUX_SAR_ADC CONFIG_MCUX_COMPONENT_driver.sar_adc)
set_variable_ifdef(CONFIG_COUNTER_MCUX_CTIMER CONFIG_MCUX_COMPONENT_driver.ctimer)
set_variable_ifdef(CONFIG_COUNTER_MCUX_LPC_RTC CONFIG_MCUX_COMPONENT_driver.lpc_rtc)
set_variable_ifdef(CONFIG_GLIKEY_MCUX_GLIKEY CONFIG_MCUX_COMPONENT_driver.glikey)
Expand Down
3 changes: 2 additions & 1 deletion soc/nxp/imx/imx9/imx93/Kconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2024 NXP
# Copyright 2024-2025 NXP
# SPDX-License-Identifier: Apache-2.0

config SOC_MIMX9352_A55
Expand All @@ -13,6 +13,7 @@ config SOC_MIMX9352_A55
config SOC_MIMX9352_M33
select ARM
select CPU_CORTEX_M33
select CPU_CORTEX_M_HAS_DWT
select CPU_HAS_FPU
select CPU_HAS_ARM_MPU
select CPU_HAS_ARM_SAU
Expand Down
Loading
Loading