This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 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 20f3b659372 arm/nrf54l: add SAADC support
20f3b659372 is described below
commit 20f3b659372c64220a2b97f43a4c1b31de5c9dd3
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:22:17 2026 +0200
arm/nrf54l: add SAADC support
arm/nrf54l: add SAADC support
Assisted-by: Codex:GPT-6
Signed-off-by: raiden00pl <[email protected]>
---
Documentation/platforms/arm/nrf54l/index.rst | 17 +-
arch/arm/src/nrf54l/CMakeLists.txt | 3 +
arch/arm/src/nrf54l/Kconfig | 67 ++
arch/arm/src/nrf54l/Make.defs | 4 +
arch/arm/src/nrf54l/hardware/nrf54l_saadc.h | 235 +++++++
arch/arm/src/nrf54l/nrf54l_adc.c | 900 +++++++++++++++++++++++++++
arch/arm/src/nrf54l/nrf54l_adc.h | 155 +++++
7 files changed, 1380 insertions(+), 1 deletion(-)
diff --git a/Documentation/platforms/arm/nrf54l/index.rst
b/Documentation/platforms/arm/nrf54l/index.rst
index 0dc99f96282..1c7340e263d 100644
--- a/Documentation/platforms/arm/nrf54l/index.rst
+++ b/Documentation/platforms/arm/nrf54l/index.rst
@@ -21,7 +21,7 @@ PWM Yes
QDEC No
RADIO Yes Bluetooth LE through SDC
RRAMC Yes Progmem erase/write interface
-SAADC No
+SAADC Yes
SPIM No
TIMER Yes
TWIM No
@@ -83,6 +83,21 @@ RRAMC
overwriting either bit value; erase operations fill emulated 4 KiB blocks
with ``0xff``. Writes require word-aligned addresses and lengths.
+SAADC
+-----
+
+``nrf54l_adcinitialize()`` provides the ADC lower half, with up to eight
+single-ended or differential channels, 8/10/12/14-bit resolution and
+optional oversampling. Each ``ANIOC_TRIGGER`` collects one result per
+channel. The local timer supports a single channel; scanning with
+oversampling requires burst mode on every channel.
+
+The internal reference is 0.9 V. Available gains range from 1/4 to 2.
+External analog and reference pins must have their digital input buffers,
+outputs and pulls disconnected before opening the device. AIN0 through
+AIN7 map to P1.04/05/06/07/11/12/13/14 on L15 and
+P1.00/31/30/29/06/05/04/03 on LM20.
+
TIMER
-----
diff --git a/arch/arm/src/nrf54l/CMakeLists.txt
b/arch/arm/src/nrf54l/CMakeLists.txt
index f3f1b86ccac..2ca9e701030 100644
--- a/arch/arm/src/nrf54l/CMakeLists.txt
+++ b/arch/arm/src/nrf54l/CMakeLists.txt
@@ -46,6 +46,9 @@ endif()
if(CONFIG_NRF54L_UART)
list(APPEND SRCS nrf54l_serial.c)
endif()
+if(CONFIG_NRF54L_SAADC)
+ list(APPEND SRCS nrf54l_adc.c)
+endif()
if(CONFIG_NRF54L_GPIOTE)
list(APPEND SRCS nrf54l_gpiote.c)
endif()
diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index 44539a84ee3..e74892745b6 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -163,6 +163,10 @@ config NRF54L_PWM2
depends on PWM
default n
+config NRF54L_SAADC
+ bool "SAADC"
+ default n
+
config NRF54L_GRTC
bool "GRTC"
default n
@@ -369,6 +373,69 @@ endif # NRF54L_PWM
endmenu # PWM configuration
+menu "SAADC Configuration"
+
+if NRF54L_SAADC
+
+choice
+ prompt "SAADC trigger selection"
+ default NRF54L_SAADC_TASK
+
+config NRF54L_SAADC_TASK
+ bool "SAADC Task trigger"
+
+config NRF54L_SAADC_TIMER
+ bool "SAADC Timer trigger"
+ ---help---
+ Use the local timer for a single channel. Each ANIOC_TRIGGER
+ collects one result and then stops the timer.
+
+endchoice # SAADC trigger selection
+
+if NRF54L_SAADC_TIMER
+
+config NRF54L_SAADC_TIMER_CC
+ int "SAADC Timer CC"
+ default 192
+ range 8 2047
+ ---help---
+ Sample rate is 16 MHz / CC. The period must accommodate the
+ channel acquisition time plus 2 us conversion time, multiplied
+ by the oversampling factor when burst is enabled.
+
+endif # NRF54L_SAADC_TIMER
+
+config NRF54L_SAADC_OVERSAMPLE
+ int "SAADC oversample"
+ default 0
+ range 0 8
+ ---help---
+ Zero disables oversampling; values 1 through 8 select 2 through
+ 256 samples per result. Scanning multiple channels requires
+ burst mode on every channel when oversampling is enabled.
+
+config NRF54L_SAADC_RESOLUTION
+ int "SAADC resolution"
+ default 0
+ range 0 3
+ ---help---
+ SAADC resolution: 0 - 8 bits, 1 - 10 bits, 2 - 12 bits, 3 - 14
bits.
+
+config NRF54L_SAADC_CHANNELS
+ int "SAADC channels"
+ default 8
+ range 1 8
+
+config NRF54L_SAADC_LIMITS
+ bool "SAADC limits enable"
+ default n
+ ---help---
+ Configure the high and low limit events for each channel.
+
+endif # NRF54L_SAADC
+
+endmenu # SAADC Configuration
+
menuconfig NRF54L_SOFTDEVICE_CONTROLLER
bool "SoftDevice Controller"
depends on ALLOW_BSDNORDIC_COMPONENTS
diff --git a/arch/arm/src/nrf54l/Make.defs b/arch/arm/src/nrf54l/Make.defs
index ded19109ce8..0654b99c67d 100644
--- a/arch/arm/src/nrf54l/Make.defs
+++ b/arch/arm/src/nrf54l/Make.defs
@@ -46,6 +46,10 @@ ifeq ($(CONFIG_NRF54L_UART),y)
CHIP_CSRCS += nrf54l_serial.c
endif
+ifeq ($(CONFIG_NRF54L_SAADC),y)
+CHIP_CSRCS += nrf54l_adc.c
+endif
+
ifeq ($(CONFIG_NRF54L_GPIOTE),y)
CHIP_CSRCS += nrf54l_gpiote.c
endif
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_saadc.h
b/arch/arm/src/nrf54l/hardware/nrf54l_saadc.h
new file mode 100644
index 00000000000..b66ab075d49
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_saadc.h
@@ -0,0 +1,235 @@
+/***************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_saadc.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_ARM_SRC_NRF54L_HARDWARE_NRF54L_SAADC_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_SAADC_H
+
+/***************************************************************************
+ * Included Files
+ ***************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf54l_memorymap.h"
+
+/***************************************************************************
+ * Pre-processor Definitions
+ ***************************************************************************/
+
+/* Register offsets for SAADC **********************************************/
+
+#define NRF54L_SAADC_TASKS_START_OFFSET 0x0000 /* Start
the SAADC */
+#define NRF54L_SAADC_TASKS_SAMPLE_OFFSET 0x0004 /* Takes
one SAADC sample */
+#define NRF54L_SAADC_TASKS_STOP_OFFSET 0x0008 /* Stop
the SAADC */
+#define NRF54L_SAADC_TASKS_CALOFFSET_OFFSET 0x000c /*
Starts offset auto-calibration */
+#define NRF54L_SAADC_SUB_START_OFFSET 0x0080 /*
Subscribe START */
+#define NRF54L_SAADC_SUB_SAMPLE_OFFSET 0x0084 /*
Subscribe SAMPLE */
+#define NRF54L_SAADC_SUB_STOP_OFFSET 0x0088 /*
Subscribe STOP */
+#define NRF54L_SAADC_SUB_CALOFFSET_OFFSET 0x008c /*
Subscribe CALIBRATEOFFSET */
+#define NRF54L_SAADC_EVENTS_STARTED_OFFSET 0x0100 /* The
SAADC has started */
+#define NRF54L_SAADC_EVENTS_END_OFFSET 0x0104 /* The
SAADC has filled up the result buffer */
+#define NRF54L_SAADC_EVENTS_DONE_OFFSET 0x0108 /* A
conversion task has been completed */
+#define NRF54L_SAADC_EVENTS_RESDONE_OFFSET 0x010c /*
Result ready for transfer to RAM */
+#define NRF54L_SAADC_EVENTS_CALDONE_OFFSET 0x0110 /*
Calibration is complete */
+#define NRF54L_SAADC_EVENTS_STOPPED_OFFSET 0x0114 /* The
SAADC has stopped */
+#define NRF54L_SAADC_EVENTS_CHLIMH_OFFSET(x) (0x118 + ((x) * 0x8)) /* Limit
high event for channel x */
+#define NRF54L_SAADC_EVENTS_CHLIML_OFFSET(x) (0x11c + ((x) * 0x8)) /* Limit
low event for channel x */
+#define NRF54L_SAADC_PUB_STARTED_OFFSET 0x0180 /*
Publish STARTED */
+#define NRF54L_SAADC_PUB_END_OFFSET 0x0184 /*
Publish END */
+#define NRF54L_SAADC_PUB_DONE_OFFSET 0x0188 /*
Publish DONE */
+#define NRF54L_SAADC_PUB_RESDONE_OFFSET 0x018c /*
Publish RESULTDONE */
+#define NRF54L_SAADC_PUB_CALDONE_OFFSET 0x0190 /*
Publish CALIBRATEDONE */
+#define NRF54L_SAADC_PUB_STOPPED_OFFSET 0x0194 /*
Publish STOPPED */
+#define NRF54L_SAADC_PUB_CHLIMH_OFFSET(x) (0x198 + ((x) * 0x8)) /*
Publish channel high limit */
+#define NRF54L_SAADC_PUB_CHLIML_OFFSET(x) (0x19c + ((x) * 0x8)) /*
Publish channel low limit */
+#define NRF54L_SAADC_SHORTS_OFFSET 0x0200 /*
Shortcuts */
+#define NRF54L_SAADC_INTEN_OFFSET 0x0300 /*
Enable or disable interrupt */
+#define NRF54L_SAADC_INTENSET_OFFSET 0x0304 /*
Enable interrupt */
+#define NRF54L_SAADC_INTENCLR_OFFSET 0x0308 /*
Disable interrupt */
+#define NRF54L_SAADC_STATUS_OFFSET 0x0400 /*
Status */
+#define NRF54L_SAADC_LINCALCOEFF_OFFSET(x) (0x440 + ((x) * 0x4)) /*
Linearity calibration coefficient */
+#define NRF54L_SAADC_ENABLE_OFFSET 0x0500 /*
Enable or disable SAADC */
+#define NRF54L_SAADC_CHPSELP_OFFSET(x) (0x510 + ((x) * 0x10)) /* Input
positive pin for CH[x] */
+#define NRF54L_SAADC_CHPSELN_OFFSET(x) (0x514 + ((x) * 0x10)) /* Input
negative pin for CH[x] */
+#define NRF54L_SAADC_CHCONFIG_OFFSET(x) (0x518 + ((x) * 0x10)) /* Input
configuration for CH[x] */
+#define NRF54L_SAADC_CHLIMIT_OFFSET(x) (0x51c + ((x) * 0x10)) /*
High/low limits for event monitoring of a CH[x] */
+#define NRF54L_SAADC_BURST_OFFSET 0x05e8 /*
Global burst control (LM20) */
+#define NRF54L_SAADC_RESOLUTION_OFFSET 0x05f0 /*
Resolution configuration */
+#define NRF54L_SAADC_OVERSAMPLE_OFFSET 0x05f4 /*
Oversampling configuration */
+#define NRF54L_SAADC_SAMPLERATE_OFFSET 0x05f8 /*
Controls normal or continuous sample rate */
+#define NRF54L_SAADC_PTR_OFFSET 0x062c /* Data
pointer */
+#define NRF54L_SAADC_MAXCNT_OFFSET 0x0630 /*
Maximum number of bytes */
+#define NRF54L_SAADC_AMOUNT_OFFSET 0x0634 /* Bytes
written at END or STOPPED */
+#define NRF54L_SAADC_CURRENTAMOUNT_OFFSET 0x0638 /* Bytes
written since START */
+#define NRF54L_SAADC_NOISESHAPE_OFFSET 0x0654 /* Noise
shaping mode */
+
+/* Register Bitfield Definitions for SAADC *********************************/
+
+/* SUBSCRIBE/PUBLISH Registers */
+
+#define SAADC_SUBPUB_CHIDX_SHIFT (0)
+#define SAADC_SUBPUB_CHIDX_MASK (0xff << SAADC_SUBPUB_CHIDX_SHIFT)
+#define SAADC_SUBPUB_ENABLE (0x80000000)
+
+/* SHORTS Register */
+
+#define SAADC_SHORT_DONE_SAMPLE (1 << 0)
+#define SAADC_SHORT_END_START (1 << 1)
+
+/* INTEN/INTENSET/INTENCLR Register */
+
+#define SAADC_INT_STARTED (1 << 0) /* Bit 0: Interrupt
for event STARTED */
+#define SAADC_INT_END (1 << 1) /* Bit 1: Interrupt
for event END */
+#define SAADC_INT_DONE (1 << 2) /* Bit 2: Interrupt
for event DONE */
+#define SAADC_INT_RESDONE (1 << 3) /* Bit 3: Interrupt
for event RESULTDONE */
+#define SAADC_INT_CALDONE (1 << 4) /* Bit 4: Interrupt
for event CALIBRATEDONE */
+#define SAADC_INT_STOPPED (1 << 5) /* Bit 5: Interrupt
for event STOPPED */
+#define SAADC_INT_CHXLIMH(x) (1 << (2 * (x) + 6))
+#define SAADC_INT_CHXLIML(x) (1 << (2 * (x) + 7))
+#define SAADC_INT_ALL (0x003fffff)
+
+/* STATUS Register */
+
+#define SAADC_STATUS_READY (0) /* Bit 0: SAADC is ready */
+#define SAADC_STATUS_BUSY (1 << 0) /* Bit 0: SAADC is busy */
+
+/* ENABLE Register */
+
+#define SAADC_ENABLE_DIS (0) /* Bit 0: Disable SAADC */
+#define SAADC_ENABLE_EN (1 << 0) /* Bit 0: Enable SAADC */
+
+/* CH[n] PSELx Register */
+
+#define SAADC_CHPSEL_PIN_SHIFT (0) /* Bits 0-4: GPIO pin */
+#define SAADC_CHPSEL_PIN_MASK (0x1f << SAADC_CHPSEL_PIN_SHIFT)
+#define SAADC_CHPSEL_PORT_SHIFT (8) /* Bits 8-11: GPIO port */
+#define SAADC_CHPSEL_PORT_MASK (0xf << SAADC_CHPSEL_PORT_SHIFT)
+#define SAADC_CHPSEL_INTERNAL_SHIFT (12) /* Bits 12-13: Internal input
*/
+#define SAADC_CHPSEL_INTERNAL_MASK (0x3 << SAADC_CHPSEL_INTERNAL_SHIFT)
+# define SAADC_CHPSEL_AVDD (0x0 << SAADC_CHPSEL_INTERNAL_SHIFT)
+# define SAADC_CHPSEL_DVDD (0x1 << SAADC_CHPSEL_INTERNAL_SHIFT)
+# define SAADC_CHPSEL_VDD (0x2 << SAADC_CHPSEL_INTERNAL_SHIFT)
+#define SAADC_CHPSEL_CONNECT_MASK (0xc0000000)
+# define SAADC_CHPSEL_NC (0x00000000)
+# define SAADC_CHPSEL_ANALOG (0x40000000)
+# define SAADC_CHPSEL_INTERNAL (0x80000000)
+
+/* CH[n] CONFIG Register */
+
+#define SAADC_CONFIG_CHOPPING_DIS (0 << 0) /* Bit 0: Chopping disabled
(LM20) */
+#define SAADC_CONFIG_CHOPPING_EN (1 << 0) /* Bit 0: Chopping enabled
(LM20) */
+#define SAADC_CONFIG_GAIN_SHIFT (8) /* Bits 8-10: Gain control */
+#define SAADC_CONFIG_GAIN_MASK (0x7 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_2 (0x0 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_1 (0x1 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_2P3 (0x2 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_1P2 (0x3 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_2P5 (0x4 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_1P3 (0x5 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_2P7 (0x6 << SAADC_CONFIG_GAIN_SHIFT)
+# define SAADC_CONFIG_GAIN_1P4 (0x7 << SAADC_CONFIG_GAIN_SHIFT)
+#define SAADC_CONFIG_BURS_DIS (0 << 11) /* Bit 11: Burst disabled
(L15) */
+#define SAADC_CONFIG_BURS_EN (1 << 11) /* Bit 11: Burst enabled
(L15) */
+#define SAADC_CONFIG_REFSEL_INTERNAL (0 << 12) /* Bit 12: Internal reference
(0.9V) */
+#define SAADC_CONFIG_REFSEL_EXTERNAL (1 << 12) /* Bit 12: External reference
*/
+#define SAADC_CONFIG_MODE_SE (0 << 15) /* Bit 15: Single-ended */
+#define SAADC_CONFIG_MODE_DIFF (1 << 15) /* Bit 15: Differential */
+#define SAADC_CONFIG_TACQ_SHIFT (16) /* Bits 16-24: Acquisition
time */
+#define SAADC_CONFIG_TACQ_MASK (0x1ff << SAADC_CONFIG_TACQ_SHIFT)
+# define SAADC_CONFIG_TACQ_3US (23 << SAADC_CONFIG_TACQ_SHIFT)
+# define SAADC_CONFIG_TACQ_5US (39 << SAADC_CONFIG_TACQ_SHIFT)
+# define SAADC_CONFIG_TACQ_10US (79 << SAADC_CONFIG_TACQ_SHIFT)
+# define SAADC_CONFIG_TACQ_15US (119 << SAADC_CONFIG_TACQ_SHIFT)
+# define SAADC_CONFIG_TACQ_20US (159 << SAADC_CONFIG_TACQ_SHIFT)
+# define SAADC_CONFIG_TACQ_40US (319 << SAADC_CONFIG_TACQ_SHIFT)
+#define SAADC_CONFIG_TCONV_SHIFT (28) /* Bits 28-30: Conversion
time */
+#define SAADC_CONFIG_TCONV_MASK (0x7 << SAADC_CONFIG_TCONV_SHIFT)
+# define SAADC_CONFIG_TCONV_2US (7 << SAADC_CONFIG_TCONV_SHIFT)
+
+/* CH[n] LIMIT Register */
+
+#define SAADC_CHLIMIT_LOW_SHIFT (0) /* Bits 0-15: Low level limit
*/
+#define SAADC_CHLIMIT_LOW_MASK (0xffff << SAADC_CHLIMIT_LOW_SHIFT)
+#define SAADC_CHLIMIT_HIGH_SHIFT (16) /* Bits 16-31: High level
limit */
+#define SAADC_CHLIMIT_HIGH_MASK (0xffff << SAADC_CHLIMIT_HIGH_SHIFT)
+
+/* BURST Register (LM20) */
+
+#define SAADC_BURST_DIS (0 << 0)
+#define SAADC_BURST_EN (1 << 0)
+
+/* RESOLUTION Register */
+
+#define SAADC_RESOLUTION_SHIFT (0) /* Bits 0-2: SAADC resolution
*/
+#define SAADC_RESOLUTION_MASK (0x7 << SAADC_RESOLUTION_SHIFT)
+# define SAADC_RESOLUTION_8BIT (0x0 << SAADC_RESOLUTION_SHIFT)
+# define SAADC_RESOLUTION_10BIT (0x1 << SAADC_RESOLUTION_SHIFT)
+# define SAADC_RESOLUTION_12BIT (0x2 << SAADC_RESOLUTION_SHIFT)
+# define SAADC_RESOLUTION_14BIT (0x3 << SAADC_RESOLUTION_SHIFT)
+
+/* OVERSAMPLE Register */
+
+#define SAADC_OVERSAMPLE_SHIFT (0) /* Bit 0-3: Oversample
control */
+#define SAADC_OVERSAMPLE_MASK (0xf << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_NONE (0x0 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_2X (0x1 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_4X (0x2 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_8X (0x3 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_16X (0x4 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_32X (0x5 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_64X (0x6 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_128X (0x7 << SAADC_OVERSAMPLE_SHIFT)
+# define SAADC_OVERSAMPLE_256X (0x8 << SAADC_OVERSAMPLE_SHIFT)
+
+/* SAMPLERATE Register */
+
+#define SAADC_SAMPLERATE_CC_SHIFT (0) /* Bits 0-10: Capture and
compare value */
+#define SAADC_SAMPLERATE_CC_MASK (0x7ff << SAADC_SAMPLERATE_CC_SHIFT)
+#define SAADC_SAMPLERATE_MODE_TASK (0 << 12) /* Bit 12: Rate is controlled
from SAMPLE task */
+#define SAADC_SAMPLERATE_MODE_TIMERS (1 << 12) /* Bit 12: Rate is controlled
from local timer */
+
+/* MAXCNT Register */
+
+#define SAADC_MAXCNT_SHIFT (0) /* Bits 0-14: Maximum byte
count */
+#define SAADC_MAXCNT_MASK (0x7fff)
+
+/* AMOUNT Register */
+
+#define SAADC_AMOUNT_SHIFT (0) /* Bits 0-14: Byte count */
+#define SAADC_AMOUNT_MASK (0x7fff)
+
+/* CURRENTAMOUNT Register */
+
+#define SAADC_CURRENTAMOUNT_MASK (0x7fff)
+
+/* TRIM.LINCALCOEFF Registers */
+
+#define SAADC_LINCALCOEFF_MASK (0xffff)
+
+/* NOISESHAPE Register */
+
+#define SAADC_NOISESHAPE_MASK (0x3)
+# define SAADC_NOISESHAPE_DISABLE (0x0)
+# define SAADC_NOISESHAPE_NS1 (0x1)
+# define SAADC_NOISESHAPE_NS2 (0x2)
+# define SAADC_NOISESHAPE_NS3 (0x3) /* LM20 only */
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_SAADC_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_adc.c b/arch/arm/src/nrf54l/nrf54l_adc.c
new file mode 100644
index 00000000000..3b51caa9f1d
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_adc.c
@@ -0,0 +1,900 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/debug.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/analog/adc.h>
+#include <nuttx/analog/ioctl.h>
+
+#include "arm_internal.h"
+#include "nrf54l_adc.h"
+
+#include "hardware/nrf54l_saadc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf54l_adc_s
+{
+ /* Upper-half callback */
+
+ const struct adc_callback_s *cb;
+
+ /* Channels configuration */
+
+ struct nrf54l_adc_channel_s channels[CONFIG_NRF54L_SAADC_CHANNELS];
+
+ /* Samples buffer */
+
+ int16_t buffer[CONFIG_NRF54L_SAADC_CHANNELS]
+ aligned_data(4);
+
+ uint8_t chan_len; /* Configured channels */
+ uint32_t base; /* Base address of ADC register */
+ uint32_t irq; /* ADC interrupt */
+ uint8_t resolution; /* ADC resolution */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* ADC Register access */
+
+static inline void nrf54l_adc_putreg(struct nrf54l_adc_s *priv,
+ uint32_t offset,
+ uint32_t value);
+static inline uint32_t nrf54l_adc_getreg(struct nrf54l_adc_s *priv,
+ uint32_t offset);
+
+/* ADC helpers */
+
+static int nrf54l_adc_configure(struct nrf54l_adc_s *priv);
+static int nrf54l_adc_calibrate(struct nrf54l_adc_s *priv);
+static uint32_t nrf54l_adc_ch_config(struct nrf54l_adc_channel_s *cfg);
+static uint32_t nrf54l_adc_chanpsel(int psel);
+static int nrf54l_adc_chancfg(struct nrf54l_adc_s *priv, uint8_t chan,
+ struct nrf54l_adc_channel_s *cfg);
+static int nrf54l_adc_isr(int irq, void *context, void *arg);
+
+/* ADC Driver Methods */
+
+static int nrf54l_adc_bind(struct adc_dev_s *dev,
+ const struct adc_callback_s *callback);
+static void nrf54l_adc_reset(struct adc_dev_s *dev);
+static int nrf54l_adc_setup(struct adc_dev_s *dev);
+static void nrf54l_adc_shutdown(struct adc_dev_s *dev);
+static void nrf54l_adc_rxint(struct adc_dev_s *dev, bool enable);
+static int nrf54l_adc_ioctl(struct adc_dev_s *dev, int cmd,
+ unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* ADC interface operations */
+
+static const struct adc_ops_s g_nrf54l_adcops =
+{
+ .ao_bind = nrf54l_adc_bind,
+ .ao_reset = nrf54l_adc_reset,
+ .ao_setup = nrf54l_adc_setup,
+ .ao_shutdown = nrf54l_adc_shutdown,
+ .ao_rxint = nrf54l_adc_rxint,
+ .ao_ioctl = nrf54l_adc_ioctl,
+};
+
+/* SAADC device */
+
+struct nrf54l_adc_s g_nrf54l_adcpriv =
+{
+ .cb = NULL,
+ .base = NRF54L_SAADC_BASE,
+ .irq = NRF54L_IRQ_SAADC,
+ .resolution = CONFIG_NRF54L_SAADC_RESOLUTION
+};
+
+/* Upper-half ADC device */
+
+static struct adc_dev_s g_nrf54l_adc =
+{
+ .ad_ops = &g_nrf54l_adcops,
+ .ad_priv = &g_nrf54l_adcpriv,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_adc_putreg
+ *
+ * Description:
+ * Put a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void nrf54l_adc_putreg(struct nrf54l_adc_s *priv,
+ uint32_t offset,
+ uint32_t value)
+{
+ DEBUGASSERT(priv);
+
+ putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_getreg
+ *
+ * Description:
+ * Get a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint32_t nrf54l_adc_getreg(struct nrf54l_adc_s *priv,
+ uint32_t offset)
+{
+ DEBUGASSERT(priv);
+
+ return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_isr
+ *
+ * Description:
+ * Common ADC interrupt service routine
+ *
+ ****************************************************************************/
+
+static int nrf54l_adc_isr(int irq, void *context, void *arg)
+{
+ struct adc_dev_s *dev = (struct adc_dev_s *) arg;
+ struct nrf54l_adc_s *priv = NULL;
+ int ret = OK;
+ int i = 0;
+
+ DEBUGASSERT(dev);
+
+ priv = (struct nrf54l_adc_s *) dev->ad_priv;
+ DEBUGASSERT(priv);
+
+ ainfo("nrf54l_adc_isr\n");
+
+ /* END event */
+
+ if (nrf54l_adc_getreg(priv, NRF54L_SAADC_EVENTS_END_OFFSET) == 1)
+ {
+ DEBUGASSERT(priv->cb != NULL);
+ DEBUGASSERT(priv->cb->au_receive != NULL);
+
+ /* Give the ADC data to the ADC driver */
+
+ for (i = 0; i < priv->chan_len; i += 1)
+ {
+ priv->cb->au_receive(dev, i, priv->buffer[i]);
+ }
+
+ /* Clear event */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_EVENTS_END_OFFSET, 0);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_configure
+ *
+ * Description:
+ * Configure ADC
+ *
+ ****************************************************************************/
+
+static int nrf54l_adc_configure(struct nrf54l_adc_s *priv)
+{
+ int regval = 0;
+
+ DEBUGASSERT(priv);
+
+ /* Configure ADC resolution */
+
+ regval = CONFIG_NRF54L_SAADC_RESOLUTION;
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_RESOLUTION_OFFSET, regval);
+
+ /* Configure oversampling */
+
+ regval = CONFIG_NRF54L_SAADC_OVERSAMPLE;
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_OVERSAMPLE_OFFSET, regval);
+
+#ifndef CONFIG_ARCH_CHIP_NRF54L15
+ /* LM20 has global burst control instead of a per-channel CONFIG bit */
+
+ regval = priv->channels[0].burst ? SAADC_BURST_EN : SAADC_BURST_DIS;
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_BURST_OFFSET, regval);
+#endif
+
+ /* Configure sample rate */
+
+#if defined(CONFIG_NRF54L_SAADC_TIMER)
+ /* Trigger from local timer */
+
+ regval = SAADC_SAMPLERATE_MODE_TIMERS;
+ regval |= ((CONFIG_NRF54L_SAADC_TIMER_CC & SAADC_SAMPLERATE_CC_MASK)
+ << SAADC_SAMPLERATE_CC_SHIFT);
+#elif defined(CONFIG_NRF54L_SAADC_TASK)
+ /* Trigger on SAMPLE tas */
+
+ regval = SAADC_SAMPLERATE_MODE_TASK;
+#else
+# error SAADC trigger not selected
+#endif
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_SAMPLERATE_OFFSET, regval);
+
+ /* Configure ADC buffer */
+
+ regval = (uintptr_t)&priv->buffer;
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_PTR_OFFSET, regval);
+
+ /* Buffer size is in bytes */
+
+ regval = priv->chan_len * sizeof(int16_t);
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_MAXCNT_OFFSET, regval);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_calibrate
+ *
+ * Description:
+ * Calibrate ADC
+ *
+ ****************************************************************************/
+
+static int nrf54l_adc_calibrate(struct nrf54l_adc_s *priv)
+{
+ /* Clear Event */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_EVENTS_CALDONE_OFFSET, 0);
+
+ /* Start calibration */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_TASKS_CALOFFSET_OFFSET, 1);
+
+ /* Wait for calibration done */
+
+ while (nrf54l_adc_getreg(priv, NRF54L_SAADC_EVENTS_CALDONE_OFFSET) != 1);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_ch_config
+ ****************************************************************************/
+
+static uint32_t nrf54l_adc_ch_config(struct nrf54l_adc_channel_s *cfg)
+{
+ uint32_t regval = 0;
+
+ /* Gain control */
+
+ switch (cfg->gain)
+ {
+ case NRF54L_ADC_GAIN_2_3:
+ {
+ regval |= SAADC_CONFIG_GAIN_2P3;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_2_5:
+ {
+ regval |= SAADC_CONFIG_GAIN_2P5;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_1_4:
+ {
+ regval |= SAADC_CONFIG_GAIN_1P4;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_1_3:
+ {
+ regval |= SAADC_CONFIG_GAIN_1P3;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_1_2:
+ {
+ regval |= SAADC_CONFIG_GAIN_1P2;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_1:
+ {
+ regval |= SAADC_CONFIG_GAIN_1;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_2:
+ {
+ regval |= SAADC_CONFIG_GAIN_2;
+ break;
+ }
+
+ case NRF54L_ADC_GAIN_2_7:
+ {
+ regval |= SAADC_CONFIG_GAIN_2P7;
+ break;
+ }
+
+ default:
+ {
+ aerr("ERROR: invalid cfg->gain: %d\n", cfg->gain);
+ }
+ }
+
+ /* Reference control */
+
+ switch (cfg->refsel)
+ {
+ case NRF54L_ADC_REFSEL_INTERNAL:
+ {
+ regval |= SAADC_CONFIG_REFSEL_INTERNAL;
+ break;
+ }
+
+ case NRF54L_ADC_REFSEL_EXTERNAL:
+ {
+ regval |= SAADC_CONFIG_REFSEL_EXTERNAL;
+ break;
+ }
+
+ default:
+ {
+ aerr("ERROR: invalid cfg->refsel: %d\n", cfg->refsel);
+ }
+ }
+
+ /* Acquisition time */
+
+ switch (cfg->tacq)
+ {
+ case NRF54L_ADC_TACQ_3US:
+ {
+ regval |= SAADC_CONFIG_TACQ_3US;
+ break;
+ }
+
+ case NRF54L_ADC_TACQ_5US:
+ {
+ regval |= SAADC_CONFIG_TACQ_5US;
+ break;
+ }
+
+ case NRF54L_ADC_TACQ_10US:
+ {
+ regval |= SAADC_CONFIG_TACQ_10US;
+ break;
+ }
+
+ case NRF54L_ADC_TACQ_15US:
+ {
+ regval |= SAADC_CONFIG_TACQ_15US;
+ break;
+ }
+
+ case NRF54L_ADC_TACQ_20US:
+ {
+ regval |= SAADC_CONFIG_TACQ_20US;
+ break;
+ }
+
+ case NRF54L_ADC_TACQ_40US:
+ {
+ regval |= SAADC_CONFIG_TACQ_40US;
+ break;
+ }
+
+ default:
+ {
+ aerr("ERROR: invalid cfg->tacq: %d\n", cfg->tacq);
+ }
+ }
+
+ /* Conversion time */
+
+ regval |= SAADC_CONFIG_TCONV_2US;
+
+ /* Singe-ended or differential mode */
+
+ switch (cfg->mode)
+ {
+ case NRF54L_ADC_MODE_SE:
+ {
+ regval |= SAADC_CONFIG_MODE_SE;
+ break;
+ }
+
+ case NRF54L_ADC_MODE_DIFF:
+ {
+ regval |= SAADC_CONFIG_MODE_DIFF;
+ break;
+ }
+
+ default:
+ {
+ aerr("ERROR: invalid cfg->mode: %d\n", cfg->mode);
+ }
+ }
+
+ /* Burst mode is configured per channel on L15 */
+
+#ifdef CONFIG_ARCH_CHIP_NRF54L15
+ switch (cfg->burst)
+ {
+ case NRF54L_ADC_BURST_DISABLE:
+ {
+ regval |= SAADC_CONFIG_BURS_DIS;
+ break;
+ }
+
+ case NRF54L_ADC_BURST_ENABLE:
+ {
+ regval |= SAADC_CONFIG_BURS_EN;
+ break;
+ }
+
+ default:
+ {
+ aerr("ERROR: invalid cfg->burst: %d\n", cfg->burst);
+ }
+ }
+#endif
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_chanpsel
+ ****************************************************************************/
+
+static uint32_t nrf54l_adc_chanpsel(int psel)
+{
+ uint32_t regval = 0;
+
+ /* AIN0 through AIN7 are located on different P1 pins on L15 and LM20 */
+
+#ifdef CONFIG_ARCH_CHIP_NRF54L15
+ static const uint8_t pins[8] =
+ {
+ 4, 5, 6, 7, 11, 12, 13, 14
+ };
+#else
+ static const uint8_t pins[8] =
+ {
+ 0, 31, 30, 29, 6, 5, 4, 3
+ };
+#endif
+
+ switch (psel)
+ {
+ case NRF54L_ADC_IN_NC:
+ {
+ regval = SAADC_CHPSEL_NC;
+ break;
+ }
+
+ case NRF54L_ADC_IN_IN0:
+ case NRF54L_ADC_IN_IN1:
+ case NRF54L_ADC_IN_IN2:
+ case NRF54L_ADC_IN_IN3:
+ case NRF54L_ADC_IN_IN4:
+ case NRF54L_ADC_IN_IN5:
+ case NRF54L_ADC_IN_IN6:
+ case NRF54L_ADC_IN_IN7:
+ {
+ regval = SAADC_CHPSEL_ANALOG | (1 << SAADC_CHPSEL_PORT_SHIFT) |
+ pins[psel - NRF54L_ADC_IN_IN0];
+ break;
+ }
+
+ case NRF54L_ADC_IN_VDD:
+ {
+ regval = SAADC_CHPSEL_INTERNAL | SAADC_CHPSEL_VDD;
+ break;
+ }
+
+ case NRF54L_ADC_IN_AVDD:
+ {
+ regval = SAADC_CHPSEL_INTERNAL | SAADC_CHPSEL_AVDD;
+ break;
+ }
+
+ case NRF54L_ADC_IN_DVDD:
+ {
+ regval = SAADC_CHPSEL_INTERNAL | SAADC_CHPSEL_DVDD;
+ break;
+ }
+
+ default:
+ {
+ aerr("ERROR: invalid psel: %d\n", psel);
+ }
+ }
+
+ return regval;
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_chancfg
+ *
+ * Description:
+ * Configure ADC channel
+ *
+ ****************************************************************************/
+
+static int nrf54l_adc_chancfg(struct nrf54l_adc_s *priv, uint8_t chan,
+ struct nrf54l_adc_channel_s *cfg)
+{
+ uint32_t regval = 0;
+ int ret = OK;
+
+ DEBUGASSERT(priv);
+
+ /* Configure positive input */
+
+ regval = nrf54l_adc_chanpsel(cfg->p_psel);
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_CHPSELP_OFFSET(chan), regval);
+
+ /* Configure negative input */
+
+ regval = nrf54l_adc_chanpsel(cfg->n_psel);
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_CHPSELN_OFFSET(chan), regval);
+
+ /* Get channel configuration */
+
+ regval = nrf54l_adc_ch_config(cfg);
+
+ /* Write channel configuration */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_CHCONFIG_OFFSET(chan), regval);
+
+#ifdef CONFIG_NRF54L_SAADC_LIMITS
+ /* Configure limits */
+
+ regval = ((uint32_t)(uint16_t)cfg->limith << SAADC_CHLIMIT_HIGH_SHIFT) |
+ ((uint32_t)(uint16_t)cfg->limitl << SAADC_CHLIMIT_LOW_SHIFT);
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_CHLIMIT_OFFSET(chan), regval);
+#endif
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_adc_bind(struct adc_dev_s *dev,
+ const struct adc_callback_s *callback)
+{
+ struct nrf54l_adc_s *priv = (struct nrf54l_adc_s *) dev->ad_priv;
+
+ DEBUGASSERT(dev);
+ DEBUGASSERT(priv);
+
+ priv->cb = callback;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_adc_reset(struct adc_dev_s *dev)
+{
+ struct nrf54l_adc_s *priv = (struct nrf54l_adc_s *) dev->ad_priv;
+
+ DEBUGASSERT(dev);
+ DEBUGASSERT(priv);
+
+ /* TODO */
+
+ UNUSED(priv);
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_adc_setup(struct adc_dev_s *dev)
+{
+ struct nrf54l_adc_s *priv = (struct nrf54l_adc_s *) dev->ad_priv;
+ int i = 0;
+ int ret = OK;
+
+ DEBUGASSERT(dev);
+ DEBUGASSERT(priv);
+
+ /* Disable ADC */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_ENABLE_OFFSET, 0);
+
+ /* Configure ADC */
+
+ ret = nrf54l_adc_configure(priv);
+ if (ret < 0)
+ {
+ aerr("ERROR: nrf54l_adc_configure failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Configure ADC channels */
+
+ for (i = 0; i < priv->chan_len; i += 1)
+ {
+ ret = nrf54l_adc_chancfg(priv, i, &priv->channels[i]);
+ if (ret < 0)
+ {
+ aerr("ERROR: chancfg failed: %d %d\n", i, ret);
+ goto errout;
+ }
+ }
+
+ /* Enable ADC */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_ENABLE_OFFSET, 1);
+
+ /* Calibrate ADC */
+
+ ret = nrf54l_adc_calibrate(priv);
+ if (ret < 0)
+ {
+ aerr("ERROR: adc calibration failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Attach the ADC interrupt */
+
+ ret = irq_attach(priv->irq, nrf54l_adc_isr, dev);
+ if (ret < 0)
+ {
+ aerr("ERROR: irq_attach failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Enable the ADC interrupt */
+
+ up_enable_irq(priv->irq);
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_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 nrf54l_adc_shutdown(struct adc_dev_s *dev)
+{
+ struct nrf54l_adc_s *priv = (struct nrf54l_adc_s *) dev->ad_priv;
+
+ DEBUGASSERT(dev);
+ DEBUGASSERT(priv);
+
+ /* Stop SAADC */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_TASKS_STOP_OFFSET, 1);
+
+ /* Wait for SAADC stopped */
+
+ while (nrf54l_adc_getreg(priv, NRF54L_SAADC_EVENTS_STOPPED_OFFSET) != 1);
+
+ /* Disable SAADC */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_ENABLE_OFFSET, 0);
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_rxint
+ *
+ * Description:
+ * Call to enable or disable RX interrupts.
+ *
+ ****************************************************************************/
+
+static void nrf54l_adc_rxint(struct adc_dev_s *dev, bool enable)
+{
+ struct nrf54l_adc_s *priv = (struct nrf54l_adc_s *) dev->ad_priv;
+ uint32_t regval = 0;
+
+ DEBUGASSERT(dev);
+ DEBUGASSERT(priv);
+
+ ainfo("RXINT enable: %d\n", enable ? 1 : 0);
+
+ regval = SAADC_INT_END;
+
+ if (enable)
+ {
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_INTENSET_OFFSET, regval);
+ }
+ else
+ {
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_INTENCLR_OFFSET, regval);
+ }
+}
+
+/****************************************************************************
+ * Name: nrf54l_adc_ioctl
+ *
+ * Description:
+ * All ioctl calls will be routed through this method.
+ *
+ ****************************************************************************/
+
+static int nrf54l_adc_ioctl(struct adc_dev_s *dev, int cmd,
+ unsigned long arg)
+{
+ struct nrf54l_adc_s *priv = (struct nrf54l_adc_s *) dev->ad_priv;
+ int ret = OK;
+
+ DEBUGASSERT(dev);
+ DEBUGASSERT(priv);
+
+ switch (cmd)
+ {
+ case ANIOC_TRIGGER:
+ {
+ /* Start ADC */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_TASKS_START_OFFSET, 1);
+
+ /* Trigger first sample */
+
+ nrf54l_adc_putreg(priv, NRF54L_SAADC_TASKS_SAMPLE_OFFSET, 1);
+ }
+ break;
+
+ case ANIOC_GET_NCHANNELS:
+ {
+ /* Return the number of configured channels */
+
+ ret = priv->chan_len;
+ }
+ break;
+
+ default:
+ {
+ aerr("ERROR: Unknown cmd: %d\n", cmd);
+ ret = -ENOTTY;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_adcinitialize
+ *
+ * Description:
+ * Initialize the ADC. See nrf54l_adc.c for more details.
+ *
+ * Input Parameters:
+ * chanlist - channels configuration
+ * nchannels - number of channels
+ *
+ * Returned Value:
+ * Valid ADC device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct adc_dev_s *nrf54l_adcinitialize(
+ const struct nrf54l_adc_channel_s *chan, int channels)
+{
+ struct adc_dev_s *dev = NULL;
+ struct nrf54l_adc_s *priv = NULL;
+ int i = 0;
+
+ DEBUGASSERT(chan != NULL);
+ DEBUGASSERT(channels <= CONFIG_NRF54L_SAADC_CHANNELS);
+
+#ifdef CONFIG_NRF54L_SAADC_TIMER
+ if (channels > 1)
+ {
+ aerr("ERROR: timer trigger works only for 1 channel!\n");
+ goto errout;
+ }
+#endif
+
+ /* Get device */
+
+ dev = &g_nrf54l_adc;
+
+ /* Get private data */
+
+ priv = (struct nrf54l_adc_s *) dev->ad_priv;
+
+ /* Copy channels configuration */
+
+ ainfo("channels: %d\n", channels);
+
+ for (i = 0; i < channels; i += 1)
+ {
+ memcpy(&priv->channels[i], &chan[i],
+ sizeof(struct nrf54l_adc_channel_s));
+ }
+
+ priv->chan_len = channels;
+
+#ifdef CONFIG_NRF54L_SAADC_TIMER
+errout:
+#endif
+ return dev;
+}
diff --git a/arch/arm/src/nrf54l/nrf54l_adc.h b/arch/arm/src/nrf54l/nrf54l_adc.h
new file mode 100644
index 00000000000..af1ab2b619e
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_adc.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_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_ARM_SRC_NRF54L_NRF54L_ADC_H
+#define __ARCH_ARM_SRC_NRF54L_NRF54L_ADC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+
+#include <nuttx/analog/adc.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* ADC input */
+
+enum nrf54l_adc_ain_e
+{
+ NRF54L_ADC_IN_NC = 0, /* Not connected */
+ NRF54L_ADC_IN_IN0 = 1, /* Analog input 0 */
+ NRF54L_ADC_IN_IN1 = 2, /* Analog input 1 */
+ NRF54L_ADC_IN_IN2 = 3, /* Analog input 2 */
+ NRF54L_ADC_IN_IN3 = 4, /* Analog input 3 */
+ NRF54L_ADC_IN_IN4 = 5, /* Analog input 4 */
+ NRF54L_ADC_IN_IN5 = 6, /* Analog input 5 */
+ NRF54L_ADC_IN_IN6 = 7, /* Analog input 6 */
+ NRF54L_ADC_IN_IN7 = 8, /* Analog input 7 */
+ NRF54L_ADC_IN_VDD = 9, /* VDD */
+ NRF54L_ADC_IN_AVDD = 10, /* Internal analog supply */
+ NRF54L_ADC_IN_DVDD = 11, /* Internal digital supply */
+};
+
+/* Gain control */
+
+enum nrf54l_adc_gain_e
+{
+ NRF54L_ADC_GAIN_2 = 0, /* 2 */
+ NRF54L_ADC_GAIN_1 = 1, /* 1 */
+ NRF54L_ADC_GAIN_2_3 = 2, /* 2/3 */
+ NRF54L_ADC_GAIN_1_2 = 3, /* 1/2 */
+ NRF54L_ADC_GAIN_2_5 = 4, /* 2/5 */
+ NRF54L_ADC_GAIN_1_3 = 5, /* 1/3 */
+ NRF54L_ADC_GAIN_2_7 = 6, /* 2/7 */
+ NRF54L_ADC_GAIN_1_4 = 7 /* 1/4 */
+};
+
+/* Reference control */
+
+enum nrf54l_adc_refsel_e
+{
+ NRF54L_ADC_REFSEL_INTERNAL = 0, /* Internal reference (0.9V) */
+ NRF54L_ADC_REFSEL_EXTERNAL = 1 /* External reference */
+};
+
+/* Acquisition time control */
+
+enum nrf54l_adc_tacq_e
+{
+ NRF54L_ADC_TACQ_3US = 0, /* 3 us */
+ NRF54L_ADC_TACQ_5US = 1, /* 5 us */
+ NRF54L_ADC_TACQ_10US = 2, /* 10 us */
+ NRF54L_ADC_TACQ_15US = 3, /* 15 us */
+ NRF54L_ADC_TACQ_20US = 4, /* 20 us */
+ NRF54L_ADC_TACQ_40US = 5 /* 40 us */
+};
+
+/* ADC mode control */
+
+enum nrf54l_adc_mode_e
+{
+ NRF54L_ADC_MODE_SE = 0, /* Single-ended mode */
+ NRF54L_ADC_MODE_DIFF = 1 /* Differential mode */
+};
+
+/* ADC burst control */
+
+enum nrf54l_adc_burst_e
+{
+ NRF54L_ADC_BURST_DISABLE = 0, /* Disable burst mode */
+ NRF54L_ADC_BURST_ENABLE = 1 /* Enable burst mode */
+};
+
+/* NRF54L ADC channel configuration */
+
+struct nrf54l_adc_channel_s
+{
+ uint32_t p_psel; /* P pin */
+ uint32_t n_psel; /* N pin */
+#ifdef CONFIG_NRF54L_SAADC_LIMITS
+ int16_t limith; /* High limit */
+ int16_t limitl; /* Low limit */
+#endif
+ uint8_t gain:3; /* Gain control */
+ uint8_t refsel:1; /* Reference control */
+ uint8_t tacq:3; /* Acquisition time */
+ uint8_t mode:1; /* Single-ended or differential mode */
+ uint8_t burst:1; /* Burst mode */
+ uint8_t _res:7; /* Reserved */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_adcinitialize
+ *
+ * Description:
+ * Initialize the ADC with a list of channel configurations. External
+ * analog and reference pins must be disconnected from digital inputs,
+ * outputs and pulls by the board before opening the device.
+ *
+ * Input Parameters:
+ * chan - Channel configurations
+ * channels - Number of channels
+ *
+ * Returned Value:
+ * Valid ADC device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct adc_dev_s *nrf54l_adcinitialize(
+ const struct nrf54l_adc_channel_s *chan,
+ int channels);
+
+#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_ADC_H */