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 */

Reply via email to