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 2022/07/07 15:45:33 UTC

[incubator-nuttx] branch master updated: Added ADC to RP2040

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


The following commit(s) were added to refs/heads/master by this push:
     new aa6ec6518c Added ADC to RP2040
aa6ec6518c is described below

commit aa6ec6518c218c71f9e205f415b76317dbc75ad3
Author: curuvar <58...@users.noreply.github.com>
AuthorDate: Thu Jul 7 07:10:58 2022 -0400

    Added ADC to RP2040
---
 .gitignore                                         |   4 +-
 arch/arm/src/rp2040/Make.defs                      |   4 +
 arch/arm/src/rp2040/hardware/rp2040_adc.h          | 120 ++++
 arch/arm/src/rp2040/rp2040_adc.c                   | 652 +++++++++++++++++++++
 .../src/rp2040/{rp2040_ws2812.h => rp2040_adc.h}   |  81 +--
 arch/arm/src/rp2040/rp2040_ws2812.h                |   2 +-
 boards/Kconfig                                     |   3 +
 .../arm/rp2040/adafruit-feather-rp2040/README.txt  |   1 +
 .../adafruit-feather-rp2040/src/rp2040_bringup.c   |  45 ++
 boards/arm/rp2040/adafruit-kb2040/README.txt       |   1 +
 .../rp2040/adafruit-kb2040/src/rp2040_bringup.c    |  45 ++
 boards/arm/rp2040/common/Kconfig                   |  54 ++
 boards/arm/rp2040/pimoroni-tiny2040/README.txt     |   1 +
 .../rp2040/pimoroni-tiny2040/src/rp2040_bringup.c  |  47 ++
 boards/arm/rp2040/raspberrypi-pico/README.txt      |   1 +
 .../rp2040/raspberrypi-pico/src/rp2040_bringup.c   |  47 ++
 drivers/analog/adc.c                               |   8 +-
 17 files changed, 1076 insertions(+), 40 deletions(-)

diff --git a/.gitignore b/.gitignore
index 658e3c09b5..f08b12a7f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,10 +26,12 @@
 *.sym
 *.wsp
 *.ddc
+*.dds
 *~
 .depend
 /.config
-/.config.old
+/.config.*
+/.config-*
 /.config\ *
 /.cproject
 /.gdbinit
diff --git a/arch/arm/src/rp2040/Make.defs b/arch/arm/src/rp2040/Make.defs
index 7bd096d8c6..6f7e129df4 100644
--- a/arch/arm/src/rp2040/Make.defs
+++ b/arch/arm/src/rp2040/Make.defs
@@ -70,6 +70,10 @@ ifeq ($(CONFIG_WS2812),y)
 CHIP_CSRCS += rp2040_ws2812.c
 endif
 
+ifeq ($(CONFIG_ADC),y)
+CHIP_CSRCS += rp2040_adc.c
+endif
+
 ifeq ($(CONFIG_RP2040_FLASH_BOOT),y)
 ifneq ($(PICO_SDK_PATH),)
 include chip/boot2/Make.defs
diff --git a/arch/arm/src/rp2040/hardware/rp2040_adc.h b/arch/arm/src/rp2040/hardware/rp2040_adc.h
new file mode 100644
index 0000000000..96225128b2
--- /dev/null
+++ b/arch/arm/src/rp2040/hardware/rp2040_adc.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/hardware/rp2040_adc.h
+ *
+ * Generated from rp2040.svd originally provided by
+ *   Raspberry Pi (Trading) Ltd.
+ *
+ * Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * 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 of the copyright holder 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 HOLDER 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_RP2040_HARDWARE_RP2040_ADC_H
+#define __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_ADC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "hardware/rp2040_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define RP2040_ADC_CS_OFFSET           0x000000  /* ADC Control and Status register */
+#define RP2040_ADC_RESULT_OFFSET       0x000004  /* ADC Results register */
+#define RP2040_ADC_FCS_OFFSET          0x000008  /* ADC FIFO Control and Status register */
+#define RP2040_ADC_FIFO_OFFSET         0x00000c  /* ADC Result FIFO */
+#define RP2040_ADC_DIV_OFFSET          0x000010  /* ADC Clock Divider register */
+#define RP2040_ADC_INTR_OFFSET         0x000014  /* ADC Raw Interrupts register */
+#define RP2040_ADC_INTE_OFFSET         0x000018  /* ADC Interrupt Enable register */
+#define RP2040_ADC_INTF_OFFSET         0x00001c  /* ADC Interrupt Force register */
+#define RP2040_ADC_INTS_OFFSET         0x000020  /* ADC Interrupt Status register */
+
+/* Register definitions *****************************************************/
+
+#define  RP2040_ADC_CS                 (RP2040_ADC_BASE + RP2040_ADC_CS_OFFSET)
+#define  RP2040_ADC_RESULT             (RP2040_ADC_BASE + RP2040_ADC_RESULT_OFFSET)
+#define  RP2040_ADC_FCS                (RP2040_ADC_BASE + RP2040_ADC_FCS_OFFSET)
+#define  RP2040_ADC_FIFO               (RP2040_ADC_BASE + RP2040_ADC_FIFO_OFFSET)
+#define  RP2040_ADC_DIV                (RP2040_ADC_BASE + RP2040_ADC_DIV_OFFSET)
+#define  RP2040_ADC_INTR               (RP2040_ADC_BASE + RP2040_ADC_INTR_OFFSET)
+#define  RP2040_ADC_INTE               (RP2040_ADC_BASE + RP2040_ADC_INTE_OFFSET)
+#define  RP2040_ADC_INTF               (RP2040_ADC_BASE + RP2040_ADC_INTF_OFFSET)
+#define  RP2040_ADC_INTS               (RP2040_ADC_BASE + RP2040_ADC_INTS_OFFSET)
+
+/* Register bit definitions *************************************************/
+
+#define RP2040_ADC_CS_RROBIN_SHIFT     (16)
+#define RP2040_ADC_CS_RROBIN_MASK      (0x001fl << RPC2040_ADC_CS_RROBIN_SHIFT)
+#define RP2040_ADC_CS_AINSEL_SHIFT     (12)
+#define RP2040_ADC_CS_AINSEL_MASK      (0x0007l << RPC2040_ADC_CS_AINSEL_SHIFT)
+#define RP2040_ADC_CS_ERR_STICKY       (1 << 10)
+#define RP2040_ADC_CS_ERR              (1 <<  9)
+#define RP2040_ADC_CS_READY            (1 <<  8)
+#define RP2040_ADC_CS_START_MANY       (1 <<  3)
+#define RP2040_ADC_CS_START_ONCE       (1 <<  2)
+#define RP2040_ADC_CS_TS_ENA           (1 <<  1)
+#define RP2040_ADC_CS_EN               (1 <<  0)
+
+#define RP2040_ADC_RESULT_VAL_MASK     (0x00000fffl)
+
+#define RP2040_ADC_FCS_THRESH_SHIFT    (24)
+#define RP2040_ADC_FCS_THRESH_MASK     (0x000fl << RP2040_ADC_FCS_THRESH_SHIFT)
+#define RP2040_ADC_FCS_LEVEL_SHIFT     (16)
+#define RP2040_ADC_FCS_LEVEL_MASK      (0x000fl << RP2040_ADC_FCS_LEVEL_SHIFT)
+#define RP2040_ADC_FCS_OVER            (1 << 11)
+#define RP2040_ADC_FCS_UNDER           (1 << 10)
+#define RP2040_ADC_FCS_FULL            (1 <<  9)
+#define RP2040_ADC_FCS_EMPTY           (1 <<  8)
+#define RP2040_ADC_FCS_DREQ_EN         (1 <<  3)
+#define RP2040_ADC_FCS_ERR             (1 <<  2)
+#define RP2040_ADC_FCS_SHIFT           (1 <<  1)
+#define RP2040_ADC_FCS_EN              (1 <<  0)
+
+#define RP2040_ADC_FIFO_ERR            (1 << 15)
+#define RP2040_ADC_FIFO_VAL_MASK       (0x0fffl)
+
+#define RP2040_ADC_DIV_INT_SHIFT       (8)
+#define RP2040_ADC_DIV_INT_MASK        (0x0fffl << RP2040_ADC_DIV_INT_SHIFT)
+#define RP2040_ADC_DIV_FRAC_SHIFT      (0)
+#define RP2040_ADC_DIV_FRAC_MASK       (0x00ffl << RP2040_ADC_DIV_FRAC_MASK)
+
+#define RP2040_ADC_INTR_FIFO           (1 << 0)  /* Raw interrupt status */
+
+#define RP2040_ADC_INTE_FIFO           (1 << 0)  /* Set to pass interrupts */
+
+#define RP2040_ADC_INTF_FIFO           (1 << 0)  /* Write 1 to force the interrupt. */
+
+#define RP2040_ADC_INTS_FIFO           (1 << 0)  /* Masked interrupt status */
+
+#endif /* __ARCH_ARM_SRC_RP2040_HARDWARE_RP2040_ADC_H */
diff --git a/arch/arm/src/rp2040/rp2040_adc.c b/arch/arm/src/rp2040/rp2040_adc.c
new file mode 100644
index 0000000000..cdbb54599a
--- /dev/null
+++ b/arch/arm/src/rp2040/rp2040_adc.c
@@ -0,0 +1,652 @@
+/****************************************************************************
+ * arch/arm/src/rp2040/rp2040_adc.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Notes:
+ *    The ADC driver upper-half returns signed values of up to 32-bits to
+ *    the user and expects the high-order bits in any result to be
+ *    significant.  So, while the RP2040 hardware returns an unsigned value
+ *    in the low-order 12 bits of the result register, we shift this
+ *    value to the high-order bits.
+ *
+ *    The result is that to convert a 32-bit value returned from the ADC
+ *    driver, you should use   V = ADC_AVDD * value / (2^31)  where ADC_AVDD
+ *    is the analogue reference voltage supplied to the RP2040 chip.  If
+ *    8 or 16 bit values are returned the divisor would be (2^15) or (2^7)
+ *    respectively.
+ *
+ *    Also, if the conversion error bit was set for a particular sample,
+ *    the return value will be negated.  Any negative return value should
+ *    be treated as erroneous.
+ *
+ *    -------------
+ *
+ *    This lower-half supports multiple drivers (/dev/adc0, /dav/dca1, etc.)
+ *    that each may read data from any of the ADC ports.  The driver reads
+ *    whichever ADC ports are needed by ANY of ther drivers in strict
+ *    round-robin fashion, passing the converted values to the drivers that
+ *    needed it.  Data is only passed if the driver is open.
+ *
+ *    --------------
+ *
+ *    This code reads the ADC ports at full speed.  At the time this comment
+ *    was written, the upper-half will throw away any converted values it
+ *    receives when the buffer is full; therefor, if the data is not read
+ *    for a while, the returned values may be stale when finally read. You
+ *    can use the ANIOC_RESET_FIFO ioctl call to flush this stale data.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <rp2040_adc.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "arm_internal.h"
+
+#include <rp2040_gpio.h>
+
+#include <hardware/rp2040_adc.h>
+
+#include <nuttx/analog/adc.h>
+#include <nuttx/kmalloc.h>
+
+#ifdef CONFIG_RP2040_ADC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ADC_CHANNEL_COUNT  5
+#define ADC_TEMP_CHANNEL   4
+
+/* Get the private data pointer from a device pointer */
+
+#define PRIV(x) ((FAR struct private_s *)(x)->ad_priv)
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+struct private_s
+{
+  FAR const struct adc_callback_s *callback;     /* ADC Callback Structure */
+  FAR FAR struct   adc_dev_s      *next_device;
+  FAR FAR struct   adc_dev_s      *prior_device;
+  bool                             has_channel[ADC_CHANNEL_COUNT];
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int  interrupt_handler(int       irq,
+                              FAR void *context,
+                              FAR void *arg);
+
+static void get_next_channel(void);
+static void add_device(FAR struct adc_dev_s *dev);
+static void remove_device(FAR struct adc_dev_s *dev);
+
+/* ADC methods */
+
+static int  my_bind(FAR struct adc_dev_s            *dev,
+                   FAR const struct adc_callback_s *callback);
+
+static void my_reset(FAR struct adc_dev_s *dev);
+static int  my_setup(FAR struct adc_dev_s *dev);
+static void my_shutdown(FAR struct adc_dev_s *dev);
+static void my_rxint(FAR struct adc_dev_s *dev, bool enable);
+static int  my_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct adc_ops_s g_adcops =
+{
+  .ao_bind     = my_bind,     /*  Called first  during initialization. */
+  .ao_reset    = my_reset,    /*  Called second during intialization.  */
+  .ao_setup    = my_setup,    /*  Called during first open.            */
+  .ao_shutdown = my_shutdown, /*  Called during last close.            */
+  .ao_rxint    = my_rxint,    /*  Called to enable/disable interrupts. */
+  .ao_ioctl    = my_ioctl,    /*  Called for custom ioctls.            */
+};
+
+static const int8_t g_gpio_map[ADC_CHANNEL_COUNT] =
+{
+  26, 27, 28, 29, -1
+};
+
+static FAR struct adc_dev_s *g_first_device     = NULL;
+
+static uint8_t               g_current_channel  = 0xf0; /* too big */
+static uint8_t               g_active_count     = 0;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: interrupt_handler
+ *
+ * Description:
+ *   ADC interrupt handler.  Note that this one handler is shared between
+ *   all ADC devices.
+ *
+ *  Note: This is called from inside an interrupt service routine.
+ ****************************************************************************/
+
+static int interrupt_handler(int irq, void *context, void *arg)
+{
+  FAR struct adc_dev_s            *a_device;
+  FAR const struct adc_callback_s *a_callback;
+  int32_t                          value;
+  bool                             error_bit_set;
+
+  if (g_active_count == 0)
+    {
+      /* Last device has been removed -- turn off ADC */
+
+      putreg32(0, RP2040_ADC_CS);
+
+      putreg32(0, RP2040_ADC_INTE);
+
+      up_disable_irq(RP2040_ADC_IRQ_FIFO);
+
+      irq_detach(RP2040_ADC_IRQ_FIFO);
+
+      /* Flush the FIFO */
+
+      while (getreg32(RP2040_ADC_FCS) & RP2040_ADC_FCS_LEVEL_MASK)
+        {
+          getreg32(RP2040_ADC_FIFO);
+        }
+
+      return OK;
+    }
+
+  /* Fetch the data from the FIFO register */
+
+  value         = getreg32(RP2040_ADC_FIFO);
+  error_bit_set = (value & RP2040_ADC_FIFO_ERR) != 0;
+
+  /* Shift value to top of signed 32-bit word for upper-halfs benefit. */
+
+  value <<= 19;
+
+  if (error_bit_set)
+    {
+      value = -value;
+    }
+
+  for (a_device = g_first_device;
+       a_device != NULL;
+       a_device = PRIV(a_device)->next_device)
+    {
+      a_callback = PRIV(a_device)->callback;
+
+      if (a_callback != NULL  &&  a_callback->au_receive != NULL)
+        {
+          if (PRIV(a_device)->has_channel[g_current_channel])
+            {
+              if (a_callback->au_receive(a_device,
+                                         g_current_channel,
+                                         value) != OK)
+                {
+                  /* ### TODO ### Upper half buffer overflow */
+                }
+            }
+        }
+    }
+
+  /* Start next channel read */
+
+  get_next_channel();
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: get_next_channel
+ *
+ * Description:
+ *   Update g_current_channel to point to next channel in use and start
+ *   the conversion.
+ *
+ *  Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void get_next_channel(void)
+{
+  FAR struct adc_dev_s *a_device;
+  uint8_t               next = g_current_channel + 1;
+  uint32_t              value;
+
+  while (true)
+    {
+      if (next >= ADC_CHANNEL_COUNT)
+        {
+          next = 0;
+        }
+
+      for (a_device = g_first_device;
+           a_device != NULL;
+           a_device = PRIV(a_device)->next_device)
+        {
+          if (PRIV(a_device)->has_channel[next])
+            {
+              g_current_channel = next;
+
+              while (getreg32(RP2040_ADC_FCS)
+                     & RP2040_ADC_FCS_LEVEL_MASK)
+                {
+                  getreg32(RP2040_ADC_FIFO);
+                }
+
+              /* Enable Interrupt on ADC Completion */
+
+              putreg32(RP2040_ADC_INTE_FIFO, RP2040_ADC_INTE);
+
+              /* Configure CS to read one value from current channel */
+
+              value =   (g_current_channel << RP2040_ADC_CS_AINSEL_SHIFT)
+                      | RP2040_ADC_CS_EN;
+
+              if (g_current_channel == ADC_TEMP_CHANNEL)
+                {
+                  value |= RP2040_ADC_CS_TS_ENA;
+                }
+
+              putreg32(value, RP2040_ADC_CS);
+
+              while ((getreg32(RP2040_ADC_CS)
+                      & RP2040_ADC_CS_READY) == 0)
+                {
+                  /* Wait for ready to go high.  The rp2040 docs
+                   * say this is only a few clock cycles so we'll
+                   * just busy wait.
+                   */
+                }
+
+              /* Start the conversion */
+
+              value += RP2040_ADC_CS_START_ONCE;
+
+              putreg32(value, RP2040_ADC_CS);
+
+              return;
+            }
+        }
+
+      /* if we've looped all the way we're in trouble */
+
+      ASSERT(next != g_current_channel);
+
+      /* try another */
+
+      next += 1;
+    }
+
+  return;
+}
+
+/****************************************************************************
+ * Name: add_device
+ *
+ * Description:
+ *   This function is called to link the device int the device list.
+ *   It also makes sure ADC reads are taking place
+ *
+ *  Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void add_device(FAR struct adc_dev_s *dev)
+{
+  uint32_t   value;
+
+  g_active_count += 1;
+
+  if (g_first_device != NULL)
+    {
+      PRIV(g_first_device)->prior_device = dev;
+    }
+
+  PRIV(dev)->next_device  = g_first_device;
+  PRIV(dev)->prior_device = NULL;
+
+  g_first_device = dev;
+
+  if (PRIV(g_first_device)->next_device == NULL)
+    {
+      /* We just added first device */
+
+      /* Make sure ADC interrupts are disabled */
+
+      putreg32(0, RP2040_ADC_INTE);
+
+      /* Configure FCS to use FIFO and interrupt on first value */
+
+      value =   (1 << RP2040_ADC_FCS_THRESH_SHIFT)
+              | RP2040_ADC_FCS_OVER
+              | RP2040_ADC_FCS_UNDER
+              | RP2040_ADC_FCS_ERR
+              | RP2040_ADC_FCS_EN;
+
+      putreg32(value, RP2040_ADC_FCS);
+
+      /* Set up for interrupts */
+
+      irq_attach(RP2040_ADC_IRQ_FIFO, interrupt_handler, NULL);
+
+      up_enable_irq(RP2040_ADC_IRQ_FIFO);
+
+      /* Start conversions on first required channel.  */
+
+      get_next_channel();
+
+      ainfo("new cur %d\n", g_current_channel);
+    }
+}
+
+/****************************************************************************
+ * Name: remove_device
+ *
+ * Description:
+ *   This function is called to unlink the device from the device list.
+ *
+ *  Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+void remove_device(FAR struct adc_dev_s *dev)
+{
+  FAR struct adc_dev_s *a_device;
+
+  if (dev == g_first_device)
+    {
+      /* Special handling for first device */
+
+      g_first_device = PRIV(g_first_device)->next_device;
+
+      if (g_first_device != NULL)
+        {
+          PRIV(g_first_device)->prior_device = NULL;
+        }
+
+      g_active_count -= 1;
+    }
+  else
+    {
+      /* Make sure dev is on the change, and unlink if it is. */
+
+      for (a_device = g_first_device;
+           a_device != NULL;
+           a_device = PRIV(a_device)->next_device)
+        {
+          if (a_device == dev)
+            {
+              PRIV(PRIV(dev)->prior_device)->next_device =
+              PRIV(dev)->next_device;
+
+              PRIV(PRIV(dev)->next_device)->prior_device =
+                PRIV(dev)->prior_device;
+
+              g_active_count -= 1;
+
+              break;
+            }
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: my_bind
+ *
+ * Description:
+ *   This function is called when a driver is registered.  It give us a
+ *   chance to bind the upper-half callbacks to our private data structure so
+ *   they can be accessed later.
+ *
+ ****************************************************************************/
+
+static int my_bind(struct adc_dev_s            *dev,
+                   const struct adc_callback_s *callback)
+{
+  DEBUGASSERT(PRIV(dev) != NULL);
+
+  ainfo("entered\n");
+
+  PRIV(dev)->callback = callback;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: my_reset
+ *
+ * Description:
+ *   This is called by the upper-half as part of the driver registration
+ *   process.  The upper half documentation also claims that it may
+ *   be called as part of an error condition.
+ *
+ *   Set the pin for the ADC's to standard GPIO input with no pulls.
+ *
+ ****************************************************************************/
+
+static void my_reset(struct adc_dev_s *dev)
+{
+  int a_gpio;
+
+  ainfo("entered\n");
+
+  for (int i = 0; i < ADC_CHANNEL_COUNT; ++i)
+    {
+      a_gpio = g_gpio_map[i];
+
+      if (a_gpio >= 0)
+        {
+          rp2040_gpio_setdir(a_gpio, false);
+          rp2040_gpio_set_function(a_gpio, RP2040_GPIO_FUNC_NULL);
+          rp2040_gpio_set_pulls(a_gpio, false, false);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: my_setup
+ *
+ * Description:
+ *   This is called when a particular ADC driver is first opened.
+ *
+ *   We don't do anything here.
+ *
+ *  Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static int my_setup(struct adc_dev_s *dev)
+{
+  int        ret;
+
+  ainfo("entered: 0x%08lX\n", dev);
+
+  /* Note: We check g_active_count here so we can return an error
+   *       in the, probably impossible, case we have too many.
+   */
+
+  if (g_active_count >= 200)
+    {
+      aerr("Too many active devices.");
+      ret = -EBUSY;
+    }
+  else
+    {
+      ret = OK;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: my_shutdown
+ *
+ * Description:
+ *  This is called to shutdown an ADC device.  It unlinks the
+ *  device from out local chain and turns off ADC interrupts if no
+ *  more devices are active.
+ *
+ *  Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void my_shutdown(FAR struct adc_dev_s *dev)
+{
+  ainfo("entered: 0x%08lX\n", dev);
+
+  /* Remove adc_dev_s structure from the list */
+
+  remove_device(dev);
+}
+
+/****************************************************************************
+ * Name: my_rxint
+ *
+ * Description:
+ *   Call to enable or disable ADC RX interrupts
+ *
+ *  Note: This is called from inside a critical section.
+ ****************************************************************************/
+
+static void my_rxint(struct adc_dev_s *dev, bool enable)
+{
+  if (enable)
+    {
+      ainfo("entered: enable: 0x%08lX\n", dev);
+
+      add_device(dev);
+    }
+  else
+    {
+      ainfo("entered: disable: 0x%08lX\n", dev);
+
+      remove_device(dev);
+    }
+}
+
+/****************************************************************************
+ * Name: my_ioctl
+ *
+ * Description:
+ *  All ioctl calls will be routed through this method
+ *
+ ****************************************************************************/
+
+static int my_ioctl(struct adc_dev_s *dev,
+                    int               cmd,
+                    unsigned long     arg)
+{
+  /* No ioctl commands supported */
+
+  ainfo("entered\n");
+
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Function
+ ****************************************************************************/
+
+#ifdef CONFIG_ADC
+
+/****************************************************************************
+ * Name: my_setup
+ *
+ * Description:
+ *   Initialize and register the ADC driver.
+ *
+ * Input Parameters:
+ *   path      - Path to the ws2812 device  (e.g. "/dev/adc0")
+ *   read_adc0 - This device reads ADC0
+ *   read_adc1 - This device reads ADC1
+ *   read_adc2 - This device reads ADC3
+ *   read_adc3 - This device reads ADC4
+ *   read_temp - This device reads the chip temperature.
+ *
+ * Returned Value:
+ *   An opaque pointer that can be passed to rp2040_adc_release on
+ *   success or NULL (with errno set) on failure
+ ****************************************************************************/
+
+int rp2040_adc_setup(FAR const char *path,
+                     bool            read_adc0,
+                     bool            read_adc1,
+                     bool            read_adc2,
+                     bool            read_adc3,
+                     bool            read_temp)
+{
+  FAR struct adc_dev_s *dev;
+  FAR struct private_s *priv;
+  int                   ret;
+
+  ainfo("entered\n");
+
+  if (!read_adc0  && !read_adc1 && !read_adc2 && !read_adc3 && !read_temp)
+    {
+      aerr("No ADC inputs selected.\n");
+      return -EINVAL;
+    }
+
+  dev = kmm_zalloc(sizeof(struct adc_dev_s));
+
+  if (dev == NULL)
+    {
+      aerr("Failed to allocate adc_dev_s.\n");
+      return -ENOMEM;
+    }
+
+  priv = kmm_zalloc(sizeof(struct private_s));
+
+  if (priv == NULL)
+    {
+      aerr("Failed to allocate private_s.\n");
+      kmm_free(dev);
+      return -ENOMEM;
+    }
+
+  priv->has_channel[0] = read_adc0;
+  priv->has_channel[1] = read_adc1;
+  priv->has_channel[2] = read_adc2;
+  priv->has_channel[3] = read_adc3;
+  priv->has_channel[4] = read_temp;
+
+  dev->ad_ops  = &g_adcops;
+  dev->ad_priv = priv;
+
+  ret = adc_register(path, dev);
+
+  return ret;
+}
+
+#endif /* if CONFIG_ADC */
+#endif /* if CONFIG_RP2040_ADC */
\ No newline at end of file
diff --git a/arch/arm/src/rp2040/rp2040_ws2812.h b/arch/arm/src/rp2040/rp2040_adc.h
similarity index 52%
copy from arch/arm/src/rp2040/rp2040_ws2812.h
copy to arch/arm/src/rp2040/rp2040_adc.h
index 533eb0dc18..10aaf82e28 100644
--- a/arch/arm/src/rp2040/rp2040_ws2812.h
+++ b/arch/arm/src/rp2040/rp2040_adc.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/arm/src/rp2040/rp2040_ws2812.h
+ * arch/arm/src/rp2040/rp2040_adc.h
  *
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,8 +18,27 @@
  *
  ****************************************************************************/
 
-#ifndef __ARCH_ARM_SRC_RP2040_RP2040_WS2812_H
-#define __ARCH_ARM_SRC_RP2040_RP2040_WS2812_H
+/****************************************************************************
+ * Note:
+ *    The ADC driver upper-half returns signed values of up to 32-bits to
+ *    the user and expects the high-order bits in any result to be
+ *    significant.  So, while the RP2040 hardware returns an unsigned value
+ *    in the low-order 12 bits of the result register, we shift this
+ *    value to the high-order bits.
+ *
+ *    The result is that to convert a 32-bit value returned from the ADC
+ *    driver, you should use   V = ADC_AVDD * value / (2^31)  where ADC_AVDD
+ *    is the analogue reference voltage supplied to the RP2040 chip.  If
+ *    8 or 16 bit values are returned the divisor would be (2^15) or (2^7)
+ *    respectively.
+ *
+ *    Also, if the conversion error bit was set for a particular sample,
+ *    the return value will be negated.  Any negative return value should
+ *    be treated as erroneous.
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_RP2040_RP2040_ADC_H
+#define __ARCH_ARM_SRC_RP2040_RP2040_ADC_H
 
 /****************************************************************************
  * Included Files
@@ -38,53 +57,46 @@ extern "C"
 #define EXTERN extern
 #endif
 
-#ifdef CONFIG_WS2812
+#ifdef CONFIG_RP2040_ADC
 
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
 
-/****************************************************************************
- * Name: rp2040_ws2812_setup
- *
- * Description:
- *   Initialize and register the ws2812 driver.
- *
- * Input Parameters:
- *   Path to the ws2812 device  (e.g. "/dev/leds0")
- *   Port number for the ws2812 chain
- *   The number of pixels in the chain
- *   Whether ws2812s have white LEDs
- *
- * Returned Value:
- *   An opaque pointer that can be passed to rp2040_ws2812_teardown on
- *   success or NULL (with errno set) on failure
- ****************************************************************************/
-
-FAR void * rp2040_ws2812_setup(FAR const char *path,
-                               int             port,
-                               uint16_t        pixel_count,
-                               bool            has_white);
+#ifdef CONFIG_ADC
 
 /****************************************************************************
- * Name: rp2040_ws2812_release
+ * Name: rp2040_adc_setup
  *
  * Description:
- *   This function releases the internal memory structures created when
- *   a driver is opened.  It will fail with an error -EBUSY the driver
- *   is open when it is called.
+ *   Initialize and register the ADC driver.
  *
  * Input Parameters:
- *   driver      - Opaque pointer returned by rp2040_ws2812_setup.
+ *   path      - Path to the ws2812 device  (e.g. "/dev/adc0")
+ *   read_adc0 - This device reads ADC0
+ *   read_adc1 - This device reads ADC1
+ *   read_adc2 - This device reads ADC3
+ *   read_adc3 - This device reads ADC4
+ *   read_temp - This device reads the chip temperature.
  *
  * Returned Value:
  *   OK on success or an ERROR on failure
- *
  ****************************************************************************/
 
-int rp2040_ws2812_release(FAR void * driver);
+int rp2040_adc_setup(FAR const char *path,
+                     bool            read_adc0,
+                     bool            read_adc1,
+                     bool            read_adc2,
+                     bool            read_adc3,
+                     bool            read_temp);
 
-#endif
+#else /* CONFIG_ADC */
+
+/* ### TODO ### Add programmatic access function. */
+
+#endif /* CONFIG_ADC */
+
+#endif /* CONFIG_RP2040_ADC */
 
 #undef EXTERN
 #if defined(__cplusplus)
@@ -92,4 +104,5 @@ int rp2040_ws2812_release(FAR void * driver);
 #endif
 
 #endif /* __ASSEMBLY__ */
-#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2C_H */
+
+#endif /* __ARCH_ARM_SRC_RP2040_RP2040_ADC_H */
diff --git a/arch/arm/src/rp2040/rp2040_ws2812.h b/arch/arm/src/rp2040/rp2040_ws2812.h
index 533eb0dc18..3f91cfc9fe 100644
--- a/arch/arm/src/rp2040/rp2040_ws2812.h
+++ b/arch/arm/src/rp2040/rp2040_ws2812.h
@@ -92,4 +92,4 @@ int rp2040_ws2812_release(FAR void * driver);
 #endif
 
 #endif /* __ASSEMBLY__ */
-#endif /* __ARCH_ARM_SRC_RP2040_RP2040_I2C_H */
+#endif /* __ARCH_ARM_SRC_RP2040_RP2040_WS2812_H */
diff --git a/boards/Kconfig b/boards/Kconfig
index c71cb1b3a1..0cb6e5fed4 100644
--- a/boards/Kconfig
+++ b/boards/Kconfig
@@ -3510,6 +3510,9 @@ endif
 if ARCH_CHIP_STM32
 source "boards/arm/stm32/common/Kconfig"
 endif
+if ARCH_CHIP_RP2040
+source "boards/arm/rp2040/common/Kconfig"
+endif
 endif
 
 config BOARD_CRASHDUMP
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/README.txt b/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
index 0ff8384310..c289a627f2 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/README.txt
@@ -14,6 +14,7 @@ Currently only the following devices are supported.
   - SPI
   - DMAC
   - PWM
+  - ADC
   - USB device
     - MSC, CDC/ACM serial and these composite device are supported.
     - CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c b/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c
index f564f478fd..c0637c8131 100644
--- a/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/adafruit-feather-rp2040/src/rp2040_bringup.c
@@ -74,6 +74,10 @@
 #define HAS_WHITE false
 #endif /* CONFIG_WS2812_HAS_WHITE */
 
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -462,5 +466,46 @@ int rp2040_bringup(void)
                       HAS_WHITE);
 #endif
 
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+#   define ADC_0 true
+# else
+#   define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+#   define ADC_1 true
+# else
+#   define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+#   define ADC_2 true
+# else
+#   define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+#   define ADC_3 true
+# else
+#   define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+#   define ADC_TEMP true
+# else
+#   define ADC_TEMP false
+# endif
+
+  ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+  if (ret != OK)
+    {
+      syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+      return ret;
+    }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
   return ret;
 }
diff --git a/boards/arm/rp2040/adafruit-kb2040/README.txt b/boards/arm/rp2040/adafruit-kb2040/README.txt
index 3bce577003..7451e2cfd3 100644
--- a/boards/arm/rp2040/adafruit-kb2040/README.txt
+++ b/boards/arm/rp2040/adafruit-kb2040/README.txt
@@ -13,6 +13,7 @@ Currently only the following devices are supported.
   - SPI
   - DMAC
   - PWM
+  - ADC
   - USB device
     - MSC, CDC/ACM serial and these composite device are supported.
     - CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c b/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c
index 2ea4c442ff..c1a071ccfe 100644
--- a/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/adafruit-kb2040/src/rp2040_bringup.c
@@ -74,6 +74,10 @@
 #define HAS_WHITE false
 #endif /* CONFIG_WS2812_HAS_WHITE */
 
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -462,5 +466,46 @@ int rp2040_bringup(void)
                       HAS_WHITE);
 #endif
 
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+#   define ADC_0 true
+# else
+#   define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+#   define ADC_1 true
+# else
+#   define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+#   define ADC_2 true
+# else
+#   define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+#   define ADC_3 true
+# else
+#   define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+#   define ADC_TEMP true
+# else
+#   define ADC_TEMP false
+# endif
+
+  ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+  if (ret != OK)
+    {
+      syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+      return ret;
+    }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
   return ret;
 }
diff --git a/boards/arm/rp2040/common/Kconfig b/boards/arm/rp2040/common/Kconfig
new file mode 100644
index 0000000000..266bebabdd
--- /dev/null
+++ b/boards/arm/rp2040/common/Kconfig
@@ -0,0 +1,54 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+if ARCH_BOARD_RASPBERRYPI_PICO
+
+config RP2040_ADC
+	bool "Enable ADC Support"
+	default n
+	---help---
+		If y, the RP2040 ADC code will be built.  For ADC device driver 
+		support you must also select "Analog-to-Digital Conversion"
+		under the "Device Driver/Analog Device Support" menu.
+
+		If the ADC device driver is not built, basic functions
+		to programatically access the ADC ports will be added.
+
+if RP2040_ADC && ADC
+
+	config RPC2040_ADC_CHANNEL0
+		bool "Read ADC channel 0"
+		default n
+		---help---
+			If y, then ADC0 will be read.
+
+	config RPC2040_ADC_CHANNEL1
+		bool "Read ADC channel 1"
+		default n
+		---help---
+			If y, then ADC1 will be read.
+
+	config RPC2040_ADC_CHANNEL2
+		bool "Read ADC channel 2"
+		default n
+		---help---
+			If y, then ADC2 will be read.
+
+		config RPC2040_ADC_CHANNEL3
+		bool "Read ADC channel 3"
+		default n
+		---help---
+			If y, then ADC3 will be read.
+
+		config RPC2040_ADC_TEMPERATURE
+		bool "Read ADC chip temperature channel"
+		default n
+		---help---
+			If y, then the ADC chip temperature
+			will be read.
+	
+	endif
+
+endif
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/README.txt b/boards/arm/rp2040/pimoroni-tiny2040/README.txt
index c742f8c037..12af598c3d 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/README.txt
+++ b/boards/arm/rp2040/pimoroni-tiny2040/README.txt
@@ -17,6 +17,7 @@ Currently only the following devices are supported.
   - SPI
   - DMAC
   - PWM
+  - ADC
   - USB device
     - MSC, CDC/ACM serial and these composite device are supported.
     - CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c b/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c
index cfeb9cc61e..8082bbf121 100644
--- a/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/pimoroni-tiny2040/src/rp2040_bringup.c
@@ -38,6 +38,10 @@
 #include "rp2040_pwmdev.h"
 #endif
 
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
 #if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
 #include "rp2040_ws2812.h"
 #endif
@@ -408,6 +412,49 @@ int rp2040_bringup(void)
 #  endif
 #endif
 
+  /* Initialize ADC */
+
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+#   define ADC_0 true
+# else
+#   define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+#   define ADC_1 true
+# else
+#   define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+#   define ADC_2 true
+# else
+#   define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+#   define ADC_3 true
+# else
+#   define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+#   define ADC_TEMP true
+# else
+#   define ADC_TEMP false
+# endif
+
+  ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+  if (ret != OK)
+    {
+      syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+      return ret;
+    }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
   /* Initialize board neo-pixel */
 
 #if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
diff --git a/boards/arm/rp2040/raspberrypi-pico/README.txt b/boards/arm/rp2040/raspberrypi-pico/README.txt
index ed799eadd3..4bd892f45c 100644
--- a/boards/arm/rp2040/raspberrypi-pico/README.txt
+++ b/boards/arm/rp2040/raspberrypi-pico/README.txt
@@ -14,6 +14,7 @@ Currently only the following devices are supported.
   - SPI
   - DMAC
   - PWM
+  - ADC
   - USB device
     - MSC, CDC/ACM serial and these composite device are supported.
     - CDC/ACM serial device can be used for the console.
diff --git a/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c b/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c
index 23face61e8..b5b19e7e1b 100644
--- a/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c
+++ b/boards/arm/rp2040/raspberrypi-pico/src/rp2040_bringup.c
@@ -64,6 +64,10 @@
 #include "rp2040_pwmdev.h"
 #endif
 
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+#include "rp2040_adc.h"
+#endif
+
 #if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
 #include "rp2040_ws2812.h"
 #endif
@@ -487,6 +491,49 @@ int rp2040_bringup(void)
     }
 #endif
 
+  /* Initialize ADC */
+
+#if defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC)
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL0
+#   define ADC_0 true
+# else
+#   define ADC_0 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL1
+#   define ADC_1 true
+# else
+#   define ADC_1 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL2
+#   define ADC_2 true
+# else
+#   define ADC_2 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_CHANNEL3
+#   define ADC_3 true
+# else
+#   define ADC_3 false
+# endif
+
+# ifdef CONFIG_RPC2040_ADC_TEMPERATURE
+#   define ADC_TEMP true
+# else
+#   define ADC_TEMP false
+# endif
+
+  ret = rp2040_adc_setup("/dev/adc0", ADC_0, ADC_1, ADC_2, ADC_3, ADC_TEMP);
+  if (ret != OK)
+    {
+      syslog(LOG_ERR, "Failed to initialize ADC Driver: %d\n", ret);
+      return ret;
+    }
+
+#endif /* defined(CONFIG_ADC) && defined(CONFIG_RP2040_ADC) */
+
   /* Initialize board neo-pixel */
 
 #if defined(CONFIG_RP2040_BOARD_HAS_WS2812) && defined(CONFIG_WS2812)
diff --git a/drivers/analog/adc.c b/drivers/analog/adc.c
index a27866d1a5..02b2f37d69 100644
--- a/drivers/analog/adc.c
+++ b/drivers/analog/adc.c
@@ -148,14 +148,14 @@ static int adc_open(FAR struct file *filep)
                   /* Finally, Enable the ADC RX interrupt */
 
                   dev->ad_ops->ao_rxint(dev, true);
-
-                  /* Save the new open count on success */
-
-                  dev->ad_ocount = tmp;
                 }
 
               leave_critical_section(flags);
             }
+
+          /* Save the new open count on success */
+
+          dev->ad_ocount = tmp;
         }
 
       nxsem_post(&dev->ad_closesem);