This is an automated email from the ASF dual-hosted git repository.
jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new c2388717398 arch/arm64/imx9: Add SAR ADC support
c2388717398 is described below
commit c2388717398c4746d9193e69dcf807eb2928a3c1
Author: Joonas Ihonen <[email protected]>
AuthorDate: Fri May 8 16:11:17 2026 +0300
arch/arm64/imx9: Add SAR ADC support
Add support for the i.MX9 SAR ADC block.
The driver provides initialization, deinitialization, channel-mask
validation, and one-shot reads for the supported ADC channels. It also
handles the ADC clock bring-up, power-up sequence, calibration, and raw
12-bit result extraction from the per-channel data registers.
Signed-off-by: Joonas Ihonen <[email protected]>
---
arch/arm64/src/imx9/CMakeLists.txt | 4 +
arch/arm64/src/imx9/Kconfig | 5 +
arch/arm64/src/imx9/Make.defs | 4 +
arch/arm64/src/imx9/imx9_sar_adc.c | 459 +++++++++++++++++++++++++++++++++++++
arch/arm64/src/imx9/imx9_sar_adc.h | 105 +++++++++
5 files changed, 577 insertions(+)
diff --git a/arch/arm64/src/imx9/CMakeLists.txt
b/arch/arm64/src/imx9/CMakeLists.txt
index 8ed03549fc4..259e3631ecf 100644
--- a/arch/arm64/src/imx9/CMakeLists.txt
+++ b/arch/arm64/src/imx9/CMakeLists.txt
@@ -105,4 +105,8 @@ if(CONFIG_IMX9_FLEXCAN)
list(APPEND SRCS imx9_flexcan.c)
endif()
+if(CONFIG_IMX9_SAR_ADC)
+ list(APPEND SRCS imx9_sar_adc.c)
+endif()
+
target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig
index 415748b957e..aeabca63abb 100644
--- a/arch/arm64/src/imx9/Kconfig
+++ b/arch/arm64/src/imx9/Kconfig
@@ -544,6 +544,11 @@ config IMX9_TPM6_PWM_CHMUX
hex "Channel mux for TPM6"
default 0x03020100
+config IMX9_SAR_ADC
+ bool "i.MX9 SAR ADC"
+ depends on ARCH_CHIP_IMX93
+ default n
+
config IMX9_USBDEV
bool
default n
diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs
index 9f95aada779..021f913e216 100644
--- a/arch/arm64/src/imx9/Make.defs
+++ b/arch/arm64/src/imx9/Make.defs
@@ -121,3 +121,7 @@ endif
ifeq ($(CONFIG_IMX9_FLEXCAN),y)
CHIP_CSRCS += imx9_flexcan.c
endif
+
+ifeq ($(CONFIG_IMX9_SAR_ADC),y)
+ CHIP_CSRCS += imx9_sar_adc.c
+endif
diff --git a/arch/arm64/src/imx9/imx9_sar_adc.c
b/arch/arm64/src/imx9/imx9_sar_adc.c
new file mode 100644
index 00000000000..5107f9dac8b
--- /dev/null
+++ b/arch/arm64/src/imx9/imx9_sar_adc.c
@@ -0,0 +1,459 @@
+/****************************************************************************
+ * arch/arm64/src/imx9/imx9_sar_adc.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <debug.h>
+#include <syslog.h>
+
+#include "arm64_internal.h"
+
+#include "imx9_sar_adc.h"
+#include "imx9_ccm.h"
+#include "hardware/imx9_ccm.h"
+#include "hardware/imx93/imx9_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define IMX9_SAR_ADC_MAX_CHANNELS 4
+#define IMX9_SAR_ADC_VALID_MASK ((1u << IMX9_SAR_ADC_MAX_CHANNELS) - 1u)
+#define IMX9_SAR_ADC_TIMEOUT_LOOPS 100000
+
+#define IMX9_SAR_ADC_MCR_OFFSET 0x0000
+#define IMX9_SAR_ADC_MSR_OFFSET 0x0004
+#define IMX9_SAR_ADC_ISR_OFFSET 0x0010
+#define IMX9_SAR_ADC_CEOCFR0_OFFSET 0x0014
+#define IMX9_SAR_ADC_CTR0_OFFSET 0x0094
+#define IMX9_SAR_ADC_NCMR0_OFFSET 0x00a4
+#define IMX9_SAR_ADC_PCDR0_OFFSET 0x0100
+#define IMX9_SAR_ADC_PCDR_STRIDE 0x0004
+
+#define IMX9_SAR_ADC_MCR_OWREN (1u << 31)
+#define IMX9_SAR_ADC_MCR_MODE (1u << 29)
+#define IMX9_SAR_ADC_MCR_NSTART (1u << 24)
+#define IMX9_SAR_ADC_MCR_CALSTART (1u << 14)
+#define IMX9_SAR_ADC_MCR_ADCLKSE (1u << 8)
+#define IMX9_SAR_ADC_MCR_ABORTCHAIN (1u << 7)
+#define IMX9_SAR_ADC_MCR_ABORT (1u << 6)
+#define IMX9_SAR_ADC_MCR_ACKO (1u << 5)
+#define IMX9_SAR_ADC_MCR_PWDN (1u << 0)
+#define IMX9_SAR_ADC_MSR_CALBUSY (1u << 29)
+#define IMX9_SAR_ADC_MSR_NSTART (1u << 24)
+
+#define IMX9_SAR_ADC_MSR_ADCSTATUS_MASK 0x7u
+#define IMX9_SAR_ADC_MSR_STATUS_IDLE 0x0u
+#define IMX9_SAR_ADC_MSR_STATUS_POWERDOWN 0x1u
+
+#define IMX9_SAR_ADC_ISR_EOC (1u << 1)
+#define IMX9_SAR_ADC_ISR_ECH (1u << 0)
+
+#define IMX9_SAR_ADC_CTR0_RESET 0x00000014u
+#define IMX9_SAR_ADC_RESULT_MASK 0x0fffu
+
+#define IMX9_SAR_ADC_REG(offset) (IMX9_ADC1_BASE + (offset))
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static bool g_imx9_sar_adc_initialized;
+static uint32_t g_imx9_sar_adc_channel_mask;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imx9_sar_adc_channel_valid
+ *
+ * Description:
+ * Check whether the requested channel number is implemented by this
+ * single-instance SAR ADC driver.
+ *
+ * Input Parameters:
+ * channel - ADC channel index to validate
+ *
+ * Returned Value:
+ * true if the channel is supported; false otherwise.
+ ****************************************************************************/
+
+static bool imx9_sar_adc_channel_valid(uint8_t channel)
+{
+ return channel < IMX9_SAR_ADC_MAX_CHANNELS;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_wait_for_idle
+ *
+ * Description:
+ * Wait until the SAR ADC is idle and not busy with either calibration or
+ * a previously started conversion.
+ *
+ * Returned Value:
+ * Zero (OK) if the ADC reaches idle state; otherwise -ETIMEDOUT.
+ ****************************************************************************/
+
+static int imx9_sar_adc_wait_for_idle(void)
+{
+ unsigned int timeout = IMX9_SAR_ADC_TIMEOUT_LOOPS;
+
+ while (timeout-- > 0)
+ {
+ uint32_t msr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MSR_OFFSET));
+ uint32_t status = msr & IMX9_SAR_ADC_MSR_ADCSTATUS_MASK;
+
+ if (((msr & IMX9_SAR_ADC_MSR_CALBUSY) == 0) &&
+ ((msr & IMX9_SAR_ADC_MSR_NSTART) == 0) &&
+ (status == IMX9_SAR_ADC_MSR_STATUS_IDLE))
+ {
+ return 0;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_power_up
+ *
+ * Description:
+ * Clear the power-down bit and wait until the ADC reports idle state.
+ *
+ * Returned Value:
+ * Zero (OK) if the ADC powers up successfully; otherwise -ETIMEDOUT.
+ ****************************************************************************/
+
+static int imx9_sar_adc_power_up(void)
+{
+ uint32_t mcr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+ unsigned int timeout = IMX9_SAR_ADC_TIMEOUT_LOOPS;
+
+ mcr &= ~IMX9_SAR_ADC_MCR_PWDN;
+ putreg32(mcr, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+
+ while (timeout-- > 0)
+ {
+ uint32_t msr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MSR_OFFSET));
+ uint32_t status = msr & IMX9_SAR_ADC_MSR_ADCSTATUS_MASK;
+
+ if (status == IMX9_SAR_ADC_MSR_STATUS_IDLE)
+ {
+ return 0;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_calibrate
+ *
+ * Description:
+ * Start a SAR ADC calibration cycle and wait for the hardware to return
+ * to idle.
+ *
+ * Returned Value:
+ * Zero (OK) on successful calibration; a negated errno on failure.
+ ****************************************************************************/
+
+static int imx9_sar_adc_calibrate(void)
+{
+ uint32_t mcr;
+ int ret;
+
+ ret = imx9_sar_adc_wait_for_idle();
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ mcr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+ mcr |= IMX9_SAR_ADC_MCR_CALSTART;
+ putreg32(mcr, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+
+ return imx9_sar_adc_wait_for_idle();
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_hw_init
+ *
+ * Description:
+ * Low-level hardware bring-up hook. Clock root/LPCG enable, pinmux, TRDC
+ * access and any calibration sequence belong here.
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure.
+ ****************************************************************************/
+
+static int imx9_sar_adc_hw_init(void)
+{
+ uint32_t mcr;
+ int ret;
+
+ /* Bring up the ADC clock tree before touching the block registers. */
+
+ ret = imx9_ccm_configure_root_clock(CCM_CR_ADC, OSC_24M, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = imx9_ccm_root_clock_on(CCM_CR_ADC, true);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = imx9_ccm_gate_on(CCM_LPCG_ADC1, true);
+ if (ret < 0)
+ {
+ imx9_ccm_root_clock_on(CCM_CR_ADC, false);
+ return ret;
+ }
+
+ ret = imx9_sar_adc_power_up();
+ if (ret < 0)
+ {
+ imx9_ccm_gate_on(CCM_LPCG_ADC1, false);
+ imx9_ccm_root_clock_on(CCM_CR_ADC, false);
+ return ret;
+ }
+
+ mcr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+ mcr &= ~(IMX9_SAR_ADC_MCR_MODE |
+ IMX9_SAR_ADC_MCR_ADCLKSE |
+ IMX9_SAR_ADC_MCR_ABORTCHAIN |
+ IMX9_SAR_ADC_MCR_ABORT |
+ IMX9_SAR_ADC_MCR_ACKO);
+ mcr |= IMX9_SAR_ADC_MCR_OWREN;
+ putreg32(mcr, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+
+ /* Keep the reset timing value initially. */
+
+ putreg32(IMX9_SAR_ADC_CTR0_RESET,
+ IMX9_SAR_ADC_REG(IMX9_SAR_ADC_CTR0_OFFSET));
+
+ ret = imx9_sar_adc_calibrate();
+ if (ret < 0)
+ {
+ imx9_ccm_gate_on(CCM_LPCG_ADC1, false);
+ imx9_ccm_root_clock_on(CCM_CR_ADC, false);
+ return ret;
+ }
+
+ /* Clear any stale status before the first conversion. */
+
+ putreg32(0xffffffffu, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_ISR_OFFSET));
+ putreg32(0xffffffffu, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_CEOCFR0_OFFSET));
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_hw_deinit
+ *
+ * Description:
+ * Shut down the single supported SAR ADC instance and disable its clocks.
+ ****************************************************************************/
+
+static void imx9_sar_adc_hw_deinit(void)
+{
+ imx9_ccm_gate_on(CCM_LPCG_ADC1, false);
+ imx9_ccm_root_clock_on(CCM_CR_ADC, false);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imx9_sar_adc_init
+ *
+ * Description:
+ * Initialize the i.MX9 SAR ADC block and record the enabled channel mask.
+ *
+ * Input Parameters:
+ * channel_mask - Bitmask of ADC channels that callers may read
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure.
+ ****************************************************************************/
+
+int imx9_sar_adc_init(uint32_t channel_mask)
+{
+ int ret;
+
+ if ((channel_mask & ~IMX9_SAR_ADC_VALID_MASK) != 0)
+ {
+ aerr("ERROR: invalid channel mask 0x%08" PRIx32 "\n", channel_mask);
+ return -EINVAL;
+ }
+
+ if (g_imx9_sar_adc_initialized)
+ {
+ g_imx9_sar_adc_channel_mask = channel_mask;
+ return 0;
+ }
+
+ ret = imx9_sar_adc_hw_init();
+ if (ret < 0)
+ {
+ aerr("ERROR: SAR ADC hw init failed: %d\n", ret);
+ return ret;
+ }
+
+ g_imx9_sar_adc_channel_mask = channel_mask;
+ g_imx9_sar_adc_initialized = true;
+ return 0;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_deinit
+ *
+ * Description:
+ * Shut down the i.MX9 SAR ADC block and clear the initialized state.
+ ****************************************************************************/
+
+void imx9_sar_adc_deinit(void)
+{
+ if (!g_imx9_sar_adc_initialized)
+ {
+ return;
+ }
+
+ imx9_sar_adc_hw_deinit();
+ g_imx9_sar_adc_channel_mask = 0;
+ g_imx9_sar_adc_initialized = false;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_read_channel
+ *
+ * Description:
+ * Perform a one-shot conversion on the requested channel and return the
+ * raw result.
+ *
+ * Input Parameters:
+ * channel - ADC channel to convert
+ * value - Location to receive the raw ADC result
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure.
+ ****************************************************************************/
+
+int imx9_sar_adc_read_channel(uint8_t channel, uint32_t *value)
+{
+ uint32_t mcr;
+ uint32_t reg_offset;
+ unsigned int timeout;
+
+ if (value == NULL)
+ {
+ return -EINVAL;
+ }
+
+ if (!g_imx9_sar_adc_initialized)
+ {
+ return -EIO;
+ }
+
+ if (!imx9_sar_adc_channel_valid(channel))
+ {
+ return -EINVAL;
+ }
+
+ if ((g_imx9_sar_adc_channel_mask & (1u << channel)) == 0)
+ {
+ return -ENODEV;
+ }
+
+ if (imx9_sar_adc_wait_for_idle() < 0)
+ {
+ return -ETIMEDOUT;
+ }
+
+ /* One-shot single-channel normal conversion on group 0. */
+
+ putreg32(1u << channel, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_NCMR0_OFFSET));
+
+ /* Clear stale end-of-conversion flags before starting the new
+ * conversion.
+ */
+
+ putreg32(IMX9_SAR_ADC_ISR_EOC | IMX9_SAR_ADC_ISR_ECH,
+ IMX9_SAR_ADC_REG(IMX9_SAR_ADC_ISR_OFFSET));
+ putreg32(1u << channel, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_CEOCFR0_OFFSET));
+
+ mcr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+ mcr &= ~IMX9_SAR_ADC_MCR_MODE;
+ mcr |= IMX9_SAR_ADC_MCR_NSTART;
+ putreg32(mcr, IMX9_SAR_ADC_REG(IMX9_SAR_ADC_MCR_OFFSET));
+
+ timeout = IMX9_SAR_ADC_TIMEOUT_LOOPS;
+ while (timeout-- > 0)
+ {
+ uint32_t isr = getreg32(IMX9_SAR_ADC_REG(IMX9_SAR_ADC_ISR_OFFSET));
+
+ if ((isr & IMX9_SAR_ADC_ISR_EOC) != 0)
+ {
+ reg_offset = IMX9_SAR_ADC_PCDR0_OFFSET +
+ ((uint32_t)channel * IMX9_SAR_ADC_PCDR_STRIDE);
+ *value = getreg32(IMX9_SAR_ADC_REG(reg_offset)) &
+ IMX9_SAR_ADC_RESULT_MASK;
+
+ putreg32(IMX9_SAR_ADC_ISR_EOC | IMX9_SAR_ADC_ISR_ECH,
+ IMX9_SAR_ADC_REG(IMX9_SAR_ADC_ISR_OFFSET));
+ putreg32(1u << channel,
+ IMX9_SAR_ADC_REG(IMX9_SAR_ADC_CEOCFR0_OFFSET));
+ return 0;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+/****************************************************************************
+ * Name: imx9_sar_adc_is_initialized
+ *
+ * Description:
+ * Report whether the single supported SAR ADC instance has been
+ * initialized.
+ *
+ * Returned Value:
+ * true if the SAR ADC is initialized; false otherwise.
+ ****************************************************************************/
+
+bool imx9_sar_adc_is_initialized(void)
+{
+ return g_imx9_sar_adc_initialized;
+}
diff --git a/arch/arm64/src/imx9/imx9_sar_adc.h
b/arch/arm64/src/imx9/imx9_sar_adc.h
new file mode 100644
index 00000000000..3972906f20c
--- /dev/null
+++ b/arch/arm64/src/imx9/imx9_sar_adc.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * arch/arm64/src/imx9/imx9_sar_adc.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM64_SRC_IMX9_IMX9_SAR_ADC_H
+#define __ARCH_ARM64_SRC_IMX9_IMX9_SAR_ADC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: imx9_sar_adc_init
+ *
+ * Description:
+ * Initialize the i.MX9 SAR ADC block for the channels selected in the
+ * channel mask.
+ *
+ * Input Parameters:
+ * channel_mask - Bitmask of ADC channels that callers intend to use
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure.
+ ****************************************************************************/
+
+int imx9_sar_adc_init(uint32_t channel_mask);
+
+/****************************************************************************
+ * Name: imx9_sar_adc_deinit
+ *
+ * Description:
+ * Deinitialize the i.MX9 SAR ADC block.
+ ****************************************************************************/
+
+void imx9_sar_adc_deinit(void);
+
+/****************************************************************************
+ * Name: imx9_sar_adc_read_channel
+ *
+ * Description:
+ * Perform a one-shot conversion on the selected ADC channel and return the
+ * raw result.
+ *
+ * Input Parameters:
+ * channel - ADC channel to convert
+ * value - Location to receive the raw ADC result
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno on failure.
+ ****************************************************************************/
+
+int imx9_sar_adc_read_channel(uint8_t channel, uint32_t *value);
+
+/****************************************************************************
+ * Name: imx9_sar_adc_is_initialized
+ *
+ * Description:
+ * Report whether the i.MX9 SAR ADC block has been initialized.
+ *
+ * Returned Value:
+ * true if the SAR ADC is initialized; false otherwise.
+ ****************************************************************************/
+
+bool imx9_sar_adc_is_initialized(void);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_SAR_ADC_H */