You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2020/09/23 16:17:29 UTC

[incubator-nuttx] 01/02: imxrt: ADC driver

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit d67bc0c3c82dd5fb4f7b2feaae1601207c1c84ab
Author: Thomas Axelsson <th...@actia.se>
AuthorDate: Tue Sep 15 14:50:19 2020 +0200

    imxrt: ADC driver
    
    Based on LPC17xx_40xx and STM32 drivers.
---
 arch/arm/src/imxrt/Kconfig                         |  18 +
 arch/arm/src/imxrt/Make.defs                       |   4 +
 arch/arm/src/imxrt/hardware/imxrt_adc.h            |   8 +-
 arch/arm/src/imxrt/hardware/imxrt_iomuxc.h         |   2 +
 .../src/imxrt/hardware/rt106x/imxrt106x_pinmux.h   |  34 ++
 arch/arm/src/imxrt/imxrt_adc.c                     | 619 +++++++++++++++++++++
 arch/arm/src/imxrt/imxrt_adc.h                     | 100 ++++
 boards/arm/imxrt/imxrt1060-evk/src/Makefile        |   4 +
 boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h |  12 +
 boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c     | 167 ++++++
 boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c |  10 +
 11 files changed, 974 insertions(+), 4 deletions(-)

diff --git a/arch/arm/src/imxrt/Kconfig b/arch/arm/src/imxrt/Kconfig
index 8290ff5..0364e47 100644
--- a/arch/arm/src/imxrt/Kconfig
+++ b/arch/arm/src/imxrt/Kconfig
@@ -163,6 +163,10 @@ config IMXRT_LPSPI
 	bool
 	default n
 
+config IMXRT_ADC
+    bool
+    default n
+
 config IMXRT_ENC
 	bool
 	default n
@@ -518,6 +522,20 @@ menuconfig IMXRT_LPSPI4
 
 endmenu # LPSPI Peripherals
 
+menu "ADC Peripherals"
+
+menuconfig IMXRT_ADC1
+    bool "ADC1"
+    default n
+    select IMXRT_ADC
+
+menuconfig IMXRT_ADC2
+    bool "ADC2"
+    default n
+    select IMXRT_ADC
+
+endmenu
+
 config IMXRT_SEMC
 	bool "Smart External Memory Controller (SEMC)"
 	default n
diff --git a/arch/arm/src/imxrt/Make.defs b/arch/arm/src/imxrt/Make.defs
index 3d3b8cf..bfcefe5 100644
--- a/arch/arm/src/imxrt/Make.defs
+++ b/arch/arm/src/imxrt/Make.defs
@@ -167,3 +167,7 @@ endif
 ifeq ($(CONFIG_IMXRT_USBDEV),y)
 CHIP_CSRCS += imxrt_usbdev.c
 endif
+
+ifeq ($(CONFIG_IMXRT_ADC),y)
+CHIP_CSRCS += imxrt_adc.c
+endif
diff --git a/arch/arm/src/imxrt/hardware/imxrt_adc.h b/arch/arm/src/imxrt/hardware/imxrt_adc.h
index de19fc8..78f04ae 100644
--- a/arch/arm/src/imxrt/hardware/imxrt_adc.h
+++ b/arch/arm/src/imxrt/hardware/imxrt_adc.h
@@ -194,10 +194,10 @@
 #define ADC_CFG_ADSTS_SHIFT                  (8)        /* Bits: 8-9  Defines the sample time duration. */
 #define ADC_CFG_ADSTS_MASK                   (3 << ADC_CFG_ADSTS_SHIFT)
 #  define ADC_CFG_ADSTS(n)                   ((uint32_t)(n) << ADC_CFG_ADSTS_SHIFT)
-#  define ADC_CFG_ADSTS_2_12                 (0 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 2 if ADLSMP=0b, 12 if ADLSMP=1b  */
-#  define ADC_CFG_ADSTS_4_16                 (1 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 4 if ADLSMP=0b, 16 if ADLSMP=1b  */
-#  define ADC_CFG_ADSTS_6_20                 (2 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 6 if ADLSMP=0b, 20 if ADLSMP=1b  */
-#  define ADC_CFG_ADSTS_8_24                 (3 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 8 if ADLSMP=0b, 24 if ADLSMP=1b  */
+#  define ADC_CFG_ADSTS_3_13                 (0 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 3 if ADLSMP=0b, 13 if ADLSMP=1b  */
+#  define ADC_CFG_ADSTS_5_17                 (1 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 5 if ADLSMP=0b, 17 if ADLSMP=1b  */
+#  define ADC_CFG_ADSTS_7_21                 (2 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 7 if ADLSMP=0b, 21 if ADLSMP=1b  */
+#  define ADC_CFG_ADSTS_9_25                 (3 << ADC_CFG_ADSTS_SHIFT)  /* Sample period (ADC clocks) = 9 if ADLSMP=0b, 25 if ADLSMP=1b  */
 #define ADC_CFG_ADHSC                        (1 << 10)  /* Bit: 10 High Speed Configuration*/
 #define ADC_CFG_REFSEL_SHIFT                 (11)       /* Bits: 11-12  Voltage Reference Selection */
 #define ADC_CFG_REFSEL_MASK                  (3 << ADC_CFG_REFSEL_SHIFT)
diff --git a/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h b/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h
index 43041f4..493bf3b 100644
--- a/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h
+++ b/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h
@@ -181,4 +181,6 @@
                                                IOMUX_SPEED_LOW )
 #define IOMUX_USBOTG_OC_DEFAULT               (IOMUX_PULL_UP_100K)
 
+#define IOMUX_ADC_DEFAULT                     (0)
+
 #endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT_IOMUXC_H */
diff --git a/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h b/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h
index 94608d0..8b71af2 100644
--- a/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h
+++ b/arch/arm/src/imxrt/hardware/rt106x/imxrt106x_pinmux.h
@@ -1081,4 +1081,38 @@
 #define GPIO_XBAR1_INOUT19_4           (GPIO_PERIPH | GPIO_ALT6 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_07_INDEX))
 #define GPIO_XBAR1_XBAR_IN02_1         (GPIO_PERIPH | GPIO_ALT3 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_EMC_00_INDEX))
 
+/* ADC */
+#define GPIO_ADC1_CH0                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_11_INDEX))
+#define GPIO_ADC1_CH1                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_12_INDEX))
+#define GPIO_ADC1_CH2                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_13_INDEX))
+#define GPIO_ADC1_CH3                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_14_INDEX))
+#define GPIO_ADC1_CH4                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B0_15_INDEX))
+#define GPIO_ADC1_CH5                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_00_INDEX))
+#define GPIO_ADC1_CH6                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_01_INDEX))
+#define GPIO_ADC1_CH7                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_02_INDEX))
+#define GPIO_ADC1_CH8                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_03_INDEX))
+#define GPIO_ADC1_CH9                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_04_INDEX))
+#define GPIO_ADC1_CH10                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_05_INDEX))
+#define GPIO_ADC1_CH11                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_06_INDEX))
+#define GPIO_ADC1_CH12                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_07_INDEX))
+#define GPIO_ADC1_CH13                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_08_INDEX))
+#define GPIO_ADC1_CH14                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_09_INDEX))
+#define GPIO_ADC1_CH15                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_10_INDEX))
+#define GPIO_ADC2_CH0                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_11_INDEX))
+#define GPIO_ADC2_CH1                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_12_INDEX))
+#define GPIO_ADC2_CH2                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_13_INDEX))
+#define GPIO_ADC2_CH3                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_14_INDEX))
+#define GPIO_ADC2_CH4                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_15_INDEX))
+#define GPIO_ADC2_CH5                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_00_INDEX))
+#define GPIO_ADC2_CH6                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_01_INDEX))
+#define GPIO_ADC2_CH7                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_02_INDEX))
+#define GPIO_ADC2_CH8                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_03_INDEX))
+#define GPIO_ADC2_CH9                  (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_04_INDEX))
+#define GPIO_ADC2_CH10                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_05_INDEX))
+#define GPIO_ADC2_CH11                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_06_INDEX))
+#define GPIO_ADC2_CH12                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_07_INDEX))
+#define GPIO_ADC2_CH13                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_08_INDEX))
+#define GPIO_ADC2_CH14                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_09_INDEX))
+#define GPIO_ADC2_CH15                 (GPIO_PERIPH | GPIO_ALT5 | GPIO_PADMUX(IMXRT_PADMUX_GPIO_AD_B1_10_INDEX))
+
 #endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_IMXRT106X_PINMUX_H */
diff --git a/arch/arm/src/imxrt/imxrt_adc.c b/arch/arm/src/imxrt/imxrt_adc.c
new file mode 100644
index 0000000..585eb60
--- /dev/null
+++ b/arch/arm/src/imxrt/imxrt_adc.c
@@ -0,0 +1,619 @@
+/****************************************************************************
+ * arch/arm/src/imxrt/imxrt_adc.c
+ *
+ *   Copyright (C) 2020 Actia Nordic AB. All rights reserved.
+ *   Author: Thomas Axelsson <th...@actia.se>
+ *
+ * Based on arch/arm/src/lpc_17xx_40xx/imxrt_adc.c
+ *
+ *   Copyright (C) 2011 Li Zhuoyi. All rights reserved.
+ *   Copyright (C) 2016 Gregory Nutt. All rights reserved.
+ *   Author: Li Zhuoyi <lz...@gmail.com>
+ *           Gregory Nutt <gn...@nuttx.org>
+ *
+ * and arch/arm/src/stm32/stm32_adc.c
+ *
+ *   Copyright (C) 2018 Gregory Nutt. All rights reserved.
+ *   Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved.
+ *   Authors: Gregory Nutt <gn...@nuttx.org>
+ *            Diego Sanchez <ds...@nx-engineering.com>
+ *            Paul Alexander Patience <pa...@polymtl.ca>
+ *            Mateusz Szafoni <ra...@railab.me>
+ *
+ * This file is a part of NuttX:
+ *
+ *   Copyright (C) 2010, 2013, 2016 Gregory Nutt. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/analog/adc.h>
+
+#include "arm_internal.h"
+#include "arm_arch.h"
+
+#include "chip.h"
+#include "hardware/imxrt_adc.h"
+#include "hardware/imxrt_pinmux.h"
+#include "imxrt_gpio.h"
+#include "imxrt_periphclks.h"
+
+#ifdef CONFIG_IMXRT_ADC
+
+/* Some ADC peripheral must be enabled */
+
+#if defined(CONFIG_IMXRT_ADC1) || defined(CONFIG_IMXRT_ADC2)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ADC_MAX_CHANNELS 16
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct imxrt_dev_s
+{
+  FAR const struct adc_callback_s *cb;  /* Upper driver callback */
+  uint8_t  intf;                        /* ADC number (i.e. ADC1, ADC2) */
+  uint32_t base;                        /* ADC register base */
+  uint8_t  initialized;                 /* ADC initialization counter */
+  int      irq;                         /* ADC IRQ number */
+  int      nchannels;                   /* Number of configured ADC channels */
+  uint8_t  chanlist[ADC_MAX_CHANNELS];  /* ADC channel list */
+  uint8_t  current;                     /* Current channel being converted */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void adc_putreg(FAR struct imxrt_dev_s *priv, uint32_t offset,
+                       uint32_t value);
+static uint32_t adc_getreg(FAR struct imxrt_dev_s *priv, uint32_t offset);
+static void adc_modifyreg(FAR struct imxrt_dev_s *priv, uint32_t offset,
+                          uint32_t clearbits, uint32_t setbits);
+
+/* ADC methods */
+
+static int  adc_bind(FAR struct adc_dev_s *dev,
+                     FAR const struct adc_callback_s *callback);
+static void adc_reset(FAR struct adc_dev_s *dev);
+static int  adc_setup(FAR struct adc_dev_s *dev);
+static void adc_shutdown(FAR struct adc_dev_s *dev);
+static void adc_rxint(FAR struct adc_dev_s *dev, bool enable);
+static int  adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
+static int  adc_interrupt(int irq, void *context, FAR void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct adc_ops_s g_adcops =
+{
+  .ao_bind     = adc_bind,
+  .ao_reset    = adc_reset,
+  .ao_setup    = adc_setup,
+  .ao_shutdown = adc_shutdown,
+  .ao_rxint    = adc_rxint,
+  .ao_ioctl    = adc_ioctl,
+};
+
+#ifdef CONFIG_IMXRT_ADC1
+static struct imxrt_dev_s g_adcpriv1 =
+{
+  .irq         = IMXRT_IRQ_ADC1,
+  .intf        = 1,
+  .initialized = 0,
+  .base        = IMXRT_ADC1_BASE,
+};
+
+static struct adc_dev_s g_adcdev1 =
+{
+  .ad_ops      = &g_adcops,
+  .ad_priv     = &g_adcpriv1,
+};
+
+gpio_pinset_t g_adcpinlist1[ADC_MAX_CHANNELS] =
+{
+    GPIO_ADC1_CH0,
+    GPIO_ADC1_CH1,
+    GPIO_ADC1_CH2,
+    GPIO_ADC1_CH3,
+    GPIO_ADC1_CH4,
+    GPIO_ADC1_CH5,
+    GPIO_ADC1_CH6,
+    GPIO_ADC1_CH7,
+    GPIO_ADC1_CH8,
+    GPIO_ADC1_CH9,
+    GPIO_ADC1_CH10,
+    GPIO_ADC1_CH11,
+    GPIO_ADC1_CH12,
+    GPIO_ADC1_CH13,
+    GPIO_ADC1_CH14,
+    GPIO_ADC1_CH15,
+};
+#endif
+
+#ifdef CONFIG_IMXRT_ADC2
+static struct imxrt_dev_s g_adcpriv2 =
+{
+  .irq         = IMXRT_IRQ_ADC2,
+  .intf        = 2,
+  .initialized = 0,
+  .base        = IMXRT_ADC2_BASE,
+};
+
+static struct adc_dev_s g_adcdev2 =
+{
+  .ad_ops      = &g_adcops,
+  .ad_priv     = &g_adcpriv2,
+};
+
+gpio_pinset_t g_adcpinlist2[ADC_MAX_CHANNELS] =
+{
+    GPIO_ADC2_CH0,
+    GPIO_ADC2_CH1,
+    GPIO_ADC2_CH2,
+    GPIO_ADC2_CH3,
+    GPIO_ADC2_CH4,
+    GPIO_ADC2_CH5,
+    GPIO_ADC2_CH6,
+    GPIO_ADC2_CH7,
+    GPIO_ADC2_CH8,
+    GPIO_ADC2_CH9,
+    GPIO_ADC2_CH10,
+    GPIO_ADC2_CH11,
+    GPIO_ADC2_CH12,
+    GPIO_ADC2_CH13,
+    GPIO_ADC2_CH14,
+    GPIO_ADC2_CH15,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void adc_putreg(FAR struct imxrt_dev_s *priv, uint32_t offset,
+                       uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+static uint32_t adc_getreg(FAR struct imxrt_dev_s *priv, uint32_t offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+static void adc_modifyreg(FAR struct imxrt_dev_s *priv, uint32_t offset,
+                          uint32_t clearbits, uint32_t setbits)
+{
+  modifyreg32(priv->base + offset, clearbits, setbits);
+}
+
+/****************************************************************************
+ * Name: adc_bind
+ *
+ * Description:
+ *   Bind the upper-half driver callbacks to the lower-half implementation.
+ *   This must be called early in order to receive ADC event notifications.
+ *
+ ****************************************************************************/
+
+static int adc_bind(FAR struct adc_dev_s *dev,
+                    FAR const struct adc_callback_s *callback)
+{
+  FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+
+  DEBUGASSERT(priv != NULL);
+  priv->cb = callback;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: adc_reset
+ *
+ * Description:
+ *   Reset the ADC device.  Called early to initialize the hardware. This
+ *   is called, before adc_setup() and on error conditions.
+ *
+ ****************************************************************************/
+
+static void adc_reset(FAR struct adc_dev_s *dev)
+{
+  FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  /* Do nothing if ADC instance is currently in use */
+
+  if (priv->initialized > 0)
+    {
+      goto exit_leave_critical;
+    }
+
+  /* Configure clock gating */
+
+  switch (priv->intf)
+    {
+#ifdef CONFIG_IMXRT_ADC1
+      case 1:
+        imxrt_clockall_adc1();
+        break;
+#endif
+#ifdef CONFIG_IMXRT_ADC2
+      case 2:
+        imxrt_clockall_adc2();
+        break;
+#endif
+      default:
+        aerr("ERROR: Tried to reset non-existing ADC: %d\n", priv->intf);
+        goto exit_leave_critical;
+    }
+
+  leave_critical_section(flags);
+
+  /* Configure ADC */
+
+  uint32_t adc_cfg = ADC_CFG_AVGS_4SMPL | ADC_CFG_ADTRG_SW |
+      ADC_CFG_REFSEL_VREF | ADC_CFG_ADSTS_7_21 | ADC_CFG_ADIV_DIV8 | \
+      ADC_CFG_ADLSMP | ADC_CFG_MODE_10BIT | ADC_CFG_ADICLK_IPGDIV2;
+  adc_putreg(priv, IMXRT_ADC_CFG_OFFSET, adc_cfg);
+
+  uint32_t adc_gc = 0;
+  adc_putreg(priv, IMXRT_ADC_GC_OFFSET, adc_gc);
+
+  /* Calibration - After ADC has been configured as desired.
+   * ADTRG in ADC_CFG must be SW (0) during calibration
+   */
+
+  /* Clear calibration error */
+
+  adc_modifyreg(priv, IMXRT_ADC_GS_OFFSET, 0, ADC_GS_CALF);
+
+  /* Start calibration */
+
+  adc_modifyreg(priv, IMXRT_ADC_GC_OFFSET, 0, ADC_GC_CAL);
+
+  while ((adc_getreg(priv, IMXRT_ADC_GC_OFFSET) & ADC_GC_CAL) != 0 &&
+      (adc_getreg(priv, IMXRT_ADC_GS_OFFSET) & ADC_GS_CALF) == 0);
+
+  if ((adc_getreg(priv, IMXRT_ADC_GS_OFFSET) & ADC_GS_CALF) != 0 ||
+      (adc_getreg(priv, IMXRT_ADC_HS_OFFSET) & ADC_HS_COCO0) == 0)
+    {
+      aerr("ERROR: ADC%d calibration failed\n", priv->intf);
+      return;
+    }
+
+  /* Clear "conversion complete" */
+
+  uint32_t adc_r0 = adc_getreg(priv, IMXRT_ADC_R0_OFFSET);
+  UNUSED(adc_r0);
+
+  /* Pad configuration */
+
+  gpio_pinset_t *pinlist = NULL;
+  switch (priv->intf)
+    {
+#ifdef CONFIG_IMXRT_ADC1
+      case 1:
+        pinlist = g_adcpinlist1;
+        break;
+#endif
+#ifdef CONFIG_IMXRT_ADC2
+      case 2:
+        pinlist = g_adcpinlist2;
+        break;
+#endif
+      default:
+        /* We have already checked the intf number earlier in this function,
+         * so we should never get here.
+         */
+
+        return;
+    }
+
+  gpio_pinset_t pinset = 0;
+  for (int i = 0; i < priv->nchannels; i++)
+    {
+      DEBUGASSERT(priv->chanlist[i] < ADC_MAX_CHANNELS);
+      pinset = pinlist[priv->chanlist[i]] | IOMUX_ADC_DEFAULT;
+      imxrt_config_gpio(pinset);
+    }
+
+  return;
+
+exit_leave_critical:
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: adc_setup
+ *
+ * Description:
+ *   Configure the ADC. This method is called the first time that the ADC
+ *   device is opened.  This will occur when the port is first opened.
+ *   This setup includes configuring and attaching ADC interrupts.
+ *   Interrupts are all disabled upon return.
+ *
+ ****************************************************************************/
+
+static int adc_setup(FAR struct adc_dev_s *dev)
+{
+  FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+
+  /* Do nothing when the ADC device is already set up */
+
+  if (priv->initialized > 0)
+    {
+      return OK;
+    }
+
+  priv->initialized++;
+
+  int ret = irq_attach(priv->irq, adc_interrupt, dev);
+  if (ret < 0)
+    {
+      ainfo("irq_attach failed: %d\n", ret);
+      return ret;
+    }
+
+  up_enable_irq(priv->irq);
+
+  /* Start the first conversion */
+
+  priv->current = 0;
+  adc_putreg(priv, IMXRT_ADC_HC0_OFFSET,
+             ADC_HC_ADCH(priv->chanlist[priv->current]));
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: adc_shutdown
+ *
+ * Description:
+ *   Disable the ADC.  This method is called when the ADC device is closed.
+ *   This method reverses the operation the setup method.
+ *
+ ****************************************************************************/
+
+static void adc_shutdown(FAR struct adc_dev_s *dev)
+{
+  FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+
+  /* Shutdown the ADC device only when not in use */
+
+  priv->initialized--;
+
+  if (priv->initialized > 0)
+    {
+      return;
+    }
+
+  /* Disable ADC interrupts, both at the level of the ADC device and at the
+   * level of the NVIC.
+   */
+
+  /* Disable interrupt and stop any on-going conversion */
+
+  adc_putreg(priv, IMXRT_ADC_HC0_OFFSET, ~ADC_HC_AIEN | ADC_HC_ADCH_DIS);
+
+  up_disable_irq(priv->irq);
+
+  /* Then detach the ADC interrupt handler. */
+
+  irq_detach(priv->irq);
+}
+
+/****************************************************************************
+ * Name: adc_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+static void adc_rxint(FAR struct adc_dev_s *dev, bool enable)
+{
+  FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+
+  if (enable)
+    {
+      adc_modifyreg(priv, IMXRT_ADC_HC0_OFFSET, 0, ADC_HC_AIEN);
+    }
+  else
+    {
+      adc_modifyreg(priv, IMXRT_ADC_HC0_OFFSET, ADC_HC_AIEN, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: adc_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method.
+ *
+ * Input Parameters:
+ *   dev - pointer to device structure used by the driver
+ *   cmd - command
+ *   arg - arguments passed with command
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg)
+{
+  /* No ioctl commands supported */
+
+  /* TODO: ANIOC_TRIGGER, for SW triggered conversion */
+
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: adc_interrupt
+ *
+ * Description:
+ *   ADC interrupt handler
+ *
+ ****************************************************************************/
+
+static int adc_interrupt(int irq, void *context, FAR void *arg)
+{
+  FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *)arg;
+  FAR struct imxrt_dev_s *priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+  int32_t data;
+
+  if ((adc_getreg(priv, IMXRT_ADC_HS_OFFSET) & ADC_HS_COCO0) != 0)
+    {
+      /* Read data. This also clears the COCO bit. */
+
+      data = (int32_t)adc_getreg(priv, IMXRT_ADC_R0_OFFSET);
+
+      if (priv->cb != NULL)
+        {
+          DEBUGASSERT(priv->cb->au_receive != NULL);
+          priv->cb->au_receive(dev, priv->chanlist[priv->current],  data);
+        }
+
+      /* Set the channel number of the next channel that will complete
+       * conversion.
+       */
+
+      priv->current++;
+
+      if (priv->current >= priv->nchannels)
+        {
+          /* Restart the conversion sequence from the beginning */
+
+          priv->current = 0;
+        }
+
+      /* Start the next conversion */
+
+      adc_modifyreg(priv, IMXRT_ADC_HC0_OFFSET, ADC_HC_ADCH_MASK,
+                    ADC_HC_ADCH(priv->chanlist[priv->current]));
+    }
+
+  /* There are no interrupt flags left to clear */
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imxrt_adcinitialize
+ *
+ * Description:
+ *   Initialize the adc
+ *
+ * Input Parameters:
+ *   intf      - ADC number (1 or 2)
+ *   chanlist  - The list of channels
+ *   nchannels - Number of channels
+ *
+ * Returned Value:
+ *   Valid can device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+FAR struct adc_dev_s *imxrt_adcinitialize(int intf,
+                                          FAR const uint8_t *chanlist,
+                                          int nchannels)
+{
+  FAR struct adc_dev_s *dev;
+  FAR struct imxrt_dev_s *priv;
+
+  DEBUGASSERT(nchannels > 0);
+
+  switch (intf)
+    {
+#ifdef CONFIG_IMXRT_ADC1
+      case 1:
+        {
+          dev = &g_adcdev1;
+          break;
+        }
+#endif /* CONFIG_IMXRT_ADC1 */
+
+#ifdef CONFIG_IMXRT_ADC2
+      case 2:
+        {
+          dev = &g_adcdev2;
+          break;
+        }
+#endif /* CONFIG_IMXRT_ADC2 */
+
+      default:
+        {
+          aerr("ERROR: Tried to initialize invalid ADC: %d\n", intf);
+          return NULL;
+        }
+    }
+
+  priv = (FAR struct imxrt_dev_s *)dev->ad_priv;
+
+  priv->nchannels = nchannels;
+  memcpy(priv->chanlist, chanlist, nchannels);
+
+  ainfo("intf: %d nchannels: %d\n", priv->intf, priv->nchannels);
+
+  return dev;
+}
+
+#endif /* CONFIG_IMXRT_ADC1 || CONFIG_IMXRT_ADC2 */
+
+#endif /* CONFIG_IMXRT_ADC */
diff --git a/arch/arm/src/imxrt/imxrt_adc.h b/arch/arm/src/imxrt/imxrt_adc.h
new file mode 100644
index 0000000..1525f51
--- /dev/null
+++ b/arch/arm/src/imxrt/imxrt_adc.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+ * arch/arm/src/lpc17xx_40xx/lpc17_40_adc.h
+ *
+ *   Copyright (C) 2020 Actia Nordic AB. All rights reserved.
+ *   Author: Thomas Axelsson <th...@actia.se>
+ *
+ * Based on arch/arm/src/lpc_17xx_40xx/imxrt_adc.h
+ *
+ *   Copyright (C) 2010, 2012, 2013 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_IMXRT_ADC_H
+#define __ARCH_ARM_SRC_IMXRT_ADC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: imxrt_adcinitialize
+ *
+ * Description:
+ *   Initialize the adc
+ *
+ * Input Parameters:
+ *   intf      - ADC number (1 or 2)
+ *   chanlist  - The list of channels
+ *   nchannels - Number of channels
+ *
+ * Returned Value:
+ *   Valid can device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_IMXRT_ADC
+FAR struct adc_dev_s *imxrt_adcinitialize(int intf,
+                                          FAR const uint8_t *chanlist,
+                                          int nchannels);
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ARCH_ARM_SRC_IMXRT_ADC_H */
diff --git a/boards/arm/imxrt/imxrt1060-evk/src/Makefile b/boards/arm/imxrt/imxrt1060-evk/src/Makefile
index 7a073dc..9efc123 100644
--- a/boards/arm/imxrt/imxrt1060-evk/src/Makefile
+++ b/boards/arm/imxrt/imxrt1060-evk/src/Makefile
@@ -73,6 +73,10 @@ ifeq ($(CONFIG_DEV_GPIO),y)
 CSRCS += imxrt_gpio.c
 endif
 
+ifeq ($(CONFIG_IMXRT_ADC),y)
+CSRCS += imxrt_adc.c
+endif
+
 ifeq ($(CONFIG_INPUT_FT5X06),y)
 CSRCS += imxrt_ft5x06.c
 endif
diff --git a/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h b/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h
index eb6e011..216b916 100644
--- a/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h
+++ b/boards/arm/imxrt/imxrt1060-evk/src/imxrt1060-evk.h
@@ -276,6 +276,18 @@ int imxrt_gpio_initialize(void);
 #endif
 
 /****************************************************************************
+ * Name: imxrt_adc_initialize
+ *
+ * Description:
+ *   Initialize ADC drivers
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_IMXRT_ADC
+int imxrt_adc_initialize(void);
+#endif
+
+/****************************************************************************
  * Name: imxrt_ft5x06_register
  *
  * Description:
diff --git a/boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c
new file mode 100644
index 0000000..119535f
--- /dev/null
+++ b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c
@@ -0,0 +1,167 @@
+/****************************************************************************
+ * boards/arm/imxrt/imxrt1060-evk/src/imxrt_adc.c
+ *
+ *   Copyright (C) 2020 Actia Nordic AB. All rights reserved.
+ *   Author: Thomas Axelsson <th...@actia.se>
+ *
+ * Based on boards/arm/lpc17xx_40xx/mbed/src/lpc17_40_adc.c
+ *
+ * Based on boards/zkit-arm-176/src/up-adc
+ *
+ *   Copyright (C) 2013 Zilogic Systems. All rights reserved.
+ *   Author: Kannan <co...@nuttx.org>
+ *
+ * Based on boards/lpc1720g-eval/src/lpc17_40_adc.c
+ *
+ *   Copyright (C) 2012, 2014, 2016 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/board.h>
+#include <nuttx/analog/adc.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "arm_arch.h"
+
+#include "imxrt_adc.h"
+
+#ifdef CONFIG_IMXRT_ADC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+#define ADC1_NCHANNELS 3
+#define ADC2_NCHANNELS 3
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const uint8_t g_chanlist1[ADC1_NCHANNELS] =
+  {
+    /* Arduino Interface pins A0 to A2 */
+
+    15,
+    0,
+    9,
+  };
+
+static const uint8_t g_chanlist2[ADC2_NCHANNELS] =
+  {
+    /* Arduino Interface pins A3 to A5 */
+
+    10,
+    6,
+    5
+  };
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imxrt_adc_setup
+ *
+ * Description:
+ *   Initialize ADC and register the ADC driver.
+ *
+ ****************************************************************************/
+
+int imxrt_adc_initialize(void)
+{
+  static bool initialized = false;
+  struct adc_dev_s *adc;
+  int ret;
+
+  /* Check if we have already initialized */
+
+  if (!initialized)
+    {
+      /* Call imxrt_adcinitialize() to get an instance of the ADC interface */
+
+#ifdef CONFIG_IMXRT_ADC1
+      adc = imxrt_adcinitialize(1, g_chanlist1, ADC1_NCHANNELS);
+      if (adc == NULL)
+        {
+          aerr("ERROR: Failed to get ADC interface for ADC1\n");
+          return -ENODEV;
+        }
+
+      /* Register the ADC driver at "/dev/adc1" */
+
+      ret = adc_register("/dev/adc1", adc);
+      if (ret < 0)
+        {
+          aerr("ERROR: adc_register adc1 failed: %d\n", ret);
+          return ret;
+        }
+#endif
+
+#ifdef CONFIG_IMXRT_ADC2
+      adc = imxrt_adcinitialize(2, g_chanlist2, ADC2_NCHANNELS);
+      if (adc == NULL)
+        {
+          aerr("ERROR: Failed to get ADC interface for ADC2\n");
+          return -ENODEV;
+        }
+
+      /* Register the ADC driver at "/dev/adc2" */
+
+      ret = adc_register("/dev/adc2", adc);
+      if (ret < 0)
+        {
+          aerr("ERROR: adc_register adc2 failed: %d\n", ret);
+          return ret;
+        }
+#endif
+
+      /* Now we are initialized */
+
+      initialized = true;
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_ADC */
diff --git a/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c
index 8584f9e..c0e07ba 100644
--- a/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c
+++ b/boards/arm/imxrt/imxrt1060-evk/src/imxrt_bringup.c
@@ -223,6 +223,16 @@ int imxrt_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_IMXRT_ADC
+  /* Initialize ADC and register the ADC driver. */
+
+  ret = imxrt_adc_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: imxrt_adc_initialize() failed: %d\n", ret);
+    }
+#endif
+
 #ifdef CONFIG_INPUT_FT5X06
   /* Initialize the FT5X06 touchscreen driver */