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 2bae5010b94 arm/nrf54l: add PWM support
2bae5010b94 is described below
commit 2bae5010b94e228ad4637615be5569940217b4bc
Author: raiden00pl <[email protected]>
AuthorDate: Sun Sep 20 16:19:29 2026 +0200
arm/nrf54l: add PWM support
arm/nrf54l: add PWM support
Assisted-by: Codex:GPT-6
Signed-off-by: raiden00pl <[email protected]>
---
Documentation/platforms/arm/nrf54l/index.rst | 9 +-
arch/arm/src/nrf54l/CMakeLists.txt | 3 +
arch/arm/src/nrf54l/Kconfig | 144 ++++++
arch/arm/src/nrf54l/Make.defs | 4 +
arch/arm/src/nrf54l/hardware/nrf54l_pwm.h | 223 +++++++++
arch/arm/src/nrf54l/nrf54l_pwm.c | 679 +++++++++++++++++++++++++++
arch/arm/src/nrf54l/nrf54l_pwm.h | 117 +++++
7 files changed, 1178 insertions(+), 1 deletion(-)
diff --git a/Documentation/platforms/arm/nrf54l/index.rst
b/Documentation/platforms/arm/nrf54l/index.rst
index b3293e5a51a..0dc99f96282 100644
--- a/Documentation/platforms/arm/nrf54l/index.rst
+++ b/Documentation/platforms/arm/nrf54l/index.rst
@@ -17,7 +17,7 @@ Peripheral Support Notes
GPIO Yes
GPIOTE Yes
GRTC Yes Counter and tickless scheduling
-PWM No
+PWM Yes
QDEC No
RADIO Yes Bluetooth LE through SDC
RRAMC Yes Progmem erase/write interface
@@ -49,6 +49,13 @@ by LFCLK, so the LFCLK source selected with
``CONFIG_NRF54L_USE_LFCLK``
determines the long-term accuracy of the system time.
With SDC enabled, channels 7 through 11 are reserved for MPSL.
+PWM
+---
+
+PWM0 through PWM2 correspond to PWM20, PWM21 and PWM22. Each instance
+supports four output channels. The board must provide an
+``NRF54L_PWMn_CHm_PIN`` definition for each enabled channel.
+
RADIO
-----
diff --git a/arch/arm/src/nrf54l/CMakeLists.txt
b/arch/arm/src/nrf54l/CMakeLists.txt
index e545c8db9e6..f3f1b86ccac 100644
--- a/arch/arm/src/nrf54l/CMakeLists.txt
+++ b/arch/arm/src/nrf54l/CMakeLists.txt
@@ -49,6 +49,9 @@ endif()
if(CONFIG_NRF54L_GPIOTE)
list(APPEND SRCS nrf54l_gpiote.c)
endif()
+if(CONFIG_NRF54L_PWM)
+ list(APPEND SRCS nrf54l_pwm.c)
+endif()
if(CONFIG_NRF54L_TIMER)
list(APPEND SRCS nrf54l_tim.c)
if(CONFIG_TIMER)
diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index e0f9107e2f6..44539a84ee3 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -33,6 +33,10 @@ config NRF54L_TIMER
bool
default n
+config NRF54L_PWM
+ bool
+ default n
+
menu "nRF54L Peripheral Selection"
config NRF54L_GPIOTE
@@ -141,6 +145,24 @@ config NRF54L_TIMER6
depends on !NRF54L_SOFTDEVICE_CONTROLLER
default n
+config NRF54L_PWM0
+ bool "PWM0 (PWM20)"
+ select NRF54L_PWM
+ depends on PWM
+ default n
+
+config NRF54L_PWM1
+ bool "PWM1 (PWM21)"
+ select NRF54L_PWM
+ depends on PWM
+ default n
+
+config NRF54L_PWM2
+ bool "PWM2 (PWM22)"
+ select NRF54L_PWM
+ depends on PWM
+ default n
+
config NRF54L_GRTC
bool "GRTC"
default n
@@ -225,6 +247,128 @@ config NRF54L_PER_PIN_INTERRUPTS
endmenu # GPIO Interrupt Configuration
+menu "PWM configuration"
+
+if NRF54L_PWM
+
+config NRF54L_PWM_MULTICHAN
+ bool "PWM Multiple Output Channels"
+ default n
+
+if NRF54L_PWM_MULTICHAN
+
+if NRF54L_PWM0
+
+config NRF54L_PWM0_CH0
+ bool "PWM0 Channel 0 Output"
+ default n
+ ---help---
+ Enables channel 0 output.
+
+config NRF54L_PWM0_CH1
+ bool "PWM0 Channel 1 Output"
+ default n
+ ---help---
+ Enables channel 1 output.
+
+config NRF54L_PWM0_CH2
+ bool "PWM0 Channel 2 Output"
+ default n
+ ---help---
+ Enables channel 2 output.
+
+config NRF54L_PWM0_CH3
+ bool "PWM0 Channel 3 Output"
+ default n
+ ---help---
+ Enables channel 3 output.
+
+endif # NRF54L_PWM0
+
+if NRF54L_PWM1
+
+config NRF54L_PWM1_CH0
+ bool "PWM1 Channel 0 Output"
+ default n
+ ---help---
+ Enables channel 0 output.
+
+config NRF54L_PWM1_CH1
+ bool "PWM1 Channel 1 Output"
+ default n
+ ---help---
+ Enables channel 1 output.
+
+config NRF54L_PWM1_CH2
+ bool "PWM1 Channel 2 Output"
+ default n
+ ---help---
+ Enables channel 2 output.
+
+config NRF54L_PWM1_CH3
+ bool "PWM1 Channel 3 Output"
+ default n
+ ---help---
+ Enables channel 3 output.
+
+endif # NRF54L_PWM1
+
+if NRF54L_PWM2
+
+config NRF54L_PWM2_CH0
+ bool "PWM2 Channel 0 Output"
+ default n
+ ---help---
+ Enables channel 0 output.
+
+config NRF54L_PWM2_CH1
+ bool "PWM2 Channel 1 Output"
+ default n
+ ---help---
+ Enables channel 1 output.
+
+config NRF54L_PWM2_CH2
+ bool "PWM2 Channel 2 Output"
+ default n
+ ---help---
+ Enables channel 2 output.
+
+config NRF54L_PWM2_CH3
+ bool "PWM2 Channel 3 Output"
+ default n
+ ---help---
+ Enables channel 3 output.
+
+endif # NRF54L_PWM2
+
+endif # NRF54L_PWM_MULTICHAN
+
+if !NRF54L_PWM_MULTICHAN
+
+config NRF54L_PWM0_CHANNEL
+ int "PWM0 Output Channel"
+ depends on NRF54L_PWM0
+ default 0
+ range 0 3
+
+config NRF54L_PWM1_CHANNEL
+ int "PWM1 Output Channel"
+ depends on NRF54L_PWM1
+ default 0
+ range 0 3
+
+config NRF54L_PWM2_CHANNEL
+ int "PWM2 Output Channel"
+ depends on NRF54L_PWM2
+ default 0
+ range 0 3
+
+endif # !NRF54L_PWM_MULTICHAN
+
+endif # NRF54L_PWM
+
+endmenu # PWM 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 fe9629b9e25..ded19109ce8 100644
--- a/arch/arm/src/nrf54l/Make.defs
+++ b/arch/arm/src/nrf54l/Make.defs
@@ -50,6 +50,10 @@ ifeq ($(CONFIG_NRF54L_GPIOTE),y)
CHIP_CSRCS += nrf54l_gpiote.c
endif
+ifeq ($(CONFIG_NRF54L_PWM),y)
+CHIP_CSRCS += nrf54l_pwm.c
+endif
+
ifeq ($(CONFIG_NRF54L_TIMER),y)
CHIP_CSRCS += nrf54l_tim.c
ifeq ($(CONFIG_TIMER),y)
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_pwm.h
b/arch/arm/src/nrf54l/hardware/nrf54l_pwm.h
new file mode 100644
index 00000000000..5d3a44a113b
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_pwm.h
@@ -0,0 +1,223 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_pwm.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_PWM_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_PWM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf54l_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NRF54L_PWM_TASKS_STOP_OFFSET 0x0004 /*
Stop PWM */
+#define NRF54L_PWM_TASKS_NEXTSTEP_OFFSET 0x0008 /*
Steps by one value in the current sequence */
+#define NRF54L_PWM_TASKS_DMA_SEQ_START_OFFSET(n) (0x0010 + 8 * (n)) /*
Start sequence n */
+#define NRF54L_PWM_SUBSCRIBE_STOP_OFFSET 0x0084 /*
Subscribe to STOP */
+#define NRF54L_PWM_SUBSCRIBE_NEXTSTEP_OFFSET 0x0088 /*
Subscribe to NEXTSTEP */
+#define NRF54L_PWM_SUBSCRIBE_DMA_SEQ_START_OFFSET(n) (0x0090 + 8 * (n)) /*
Subscribe to sequence n START */
+#define NRF54L_PWM_EVENTS_STOPPED_OFFSET 0x0104 /*
STOP event */
+#define NRF54L_PWM_EVENTS_SEQSTARTED0_OFFSET 0x0108 /*
Sequence 0 started event */
+#define NRF54L_PWM_EVENTS_SEQSTARTED1_OFFSET 0x010c /*
Sequence 1 started event */
+#define NRF54L_PWM_EVENTS_SEQEND0_OFFSET 0x0110 /*
Sequence 0 end event */
+#define NRF54L_PWM_EVENTS_SEQEND1_OFFSET 0x0114 /*
Sequence 1 end event */
+#define NRF54L_PWM_EVENTS_PWMPERIODEND_OFFSET 0x0118 /*
PWM period end event */
+#define NRF54L_PWM_EVENTS_LOOPSDONE_OFFSET 0x011c /*
Loop done event */
+#define NRF54L_PWM_EVENTS_RAMUNDERFLOW_OFFSET 0x0120 /*
RAM underflow */
+#define NRF54L_PWM_EVENTS_DMA_SEQ_END_OFFSET(n) (0x0124 + 12 * (n)) /*
Sequence n DMA buffer completed */
+#define NRF54L_PWM_EVENTS_DMA_SEQ_READY_OFFSET(n) (0x0128 + 12 * (n)) /*
Sequence n DMA buffer prepared */
+#define NRF54L_PWM_EVENTS_DMA_SEQ_BUSERROR_OFFSET(n) (0x012c + 12 * (n)) /*
Sequence n DMA bus error */
+#define NRF54L_PWM_EVENTS_COMPAREMATCH_OFFSET(n) (0x013c + 4 * (n)) /*
Compare match on PWM channel n */
+#define NRF54L_PWM_PUBLISH_STOPPED_OFFSET 0x0184 /*
Publish STOPPED */
+#define NRF54L_PWM_PUBLISH_SEQSTARTED_OFFSET(n) (0x0188 + 4 * (n)) /*
Publish sequence n started event */
+#define NRF54L_PWM_PUBLISH_SEQEND_OFFSET(n) (0x0190 + 4 * (n)) /*
Publish sequence n end event */
+#define NRF54L_PWM_PUBLISH_PWMPERIODEND_OFFSET 0x0198 /*
Publish PWMPERIODEND */
+#define NRF54L_PWM_PUBLISH_LOOPSDONE_OFFSET 0x019c /*
Publish LOOPSDONE */
+#define NRF54L_PWM_PUBLISH_RAMUNDERFLOW_OFFSET 0x01a0 /*
Publish RAMUNDERFLOW */
+#define NRF54L_PWM_PUBLISH_DMA_SEQ_END_OFFSET(n) (0x01a4 + 12 * (n)) /*
Publish sequence n DMA END event */
+#define NRF54L_PWM_PUBLISH_DMA_SEQ_READY_OFFSET(n) (0x01a8 + 12 * (n)) /*
Publish sequence n DMA READY event */
+#define NRF54L_PWM_PUBLISH_DMA_SEQ_BUSERROR_OFFSET(n) (0x01ac + 12 * (n)) /*
Publish sequence n DMA BUSERROR event */
+#define NRF54L_PWM_PUBLISH_COMPAREMATCH_OFFSET(n) (0x01bc + 4 * (n)) /*
Publish channel n COMPAREMATCH event */
+#define NRF54L_PWM_SHORTS_OFFSET 0x0200 /*
Shortcut register */
+#define NRF54L_PWM_INTEN_OFFSET 0x0300 /*
Enable or disable interrupt */
+#define NRF54L_PWM_INTENSET_OFFSET 0x0304 /*
Enable interrupt */
+#define NRF54L_PWM_INTENCLR_OFFSET 0x0308 /*
Disable interrupt */
+#define NRF54L_PWM_INTPEND_OFFSET 0x030c /*
Pending interrupts */
+#define NRF54L_PWM_ENABLE_OFFSET 0x0500 /*
PWM enable */
+#define NRF54L_PWM_MODE_OFFSET 0x0504 /*
Wave counter mode */
+#define NRF54L_PWM_COUNTERTOP_OFFSET 0x0508 /*
Counter max value */
+#define NRF54L_PWM_PRESCALER_OFFSET 0x050c /*
Prescaler configuration */
+#define NRF54L_PWM_DECODER_OFFSET 0x0510 /*
Configuration of the decoder */
+#define NRF54L_PWM_LOOP_OFFSET 0x0514 /*
Amount of playback of a loop */
+#define NRF54L_PWM_IDLEOUT_OFFSET 0x0518 /*
Idle output levels */
+#define NRF54L_PWM_SEQ0REFRESH_OFFSET 0x0528 /*
Sequence 0 additional periods */
+#define NRF54L_PWM_SEQ0ENDDELAY_OFFSET 0x052c /*
Time added after sequence 0 */
+#define NRF54L_PWM_SEQ1REFRESH_OFFSET 0x0548 /*
Sequence 1 additional periods */
+#define NRF54L_PWM_SEQ1ENDDELAY_OFFSET 0x054c /*
Time added after sequence 1 */
+#define NRF54L_PWM_PSEL0_OFFSET 0x0560 /*
Output pin select for PWM chan 0 */
+#define NRF54L_PWM_PSEL1_OFFSET 0x0564 /*
Output pin select for PWM chan 1 */
+#define NRF54L_PWM_PSEL2_OFFSET 0x0568 /*
Output pin select for PWM chan 2 */
+#define NRF54L_PWM_PSEL3_OFFSET 0x056c /*
Output pin select for PWM chan 3 */
+
+#define NRF54L_PWM_DMA_SEQ_PTR_OFFSET(n) (0x0704 + 36 * (n))
/* Sequence n buffer address */
+#define NRF54L_PWM_DMA_SEQ_MAXCNT_OFFSET(n) (0x0708 + 36 * (n))
/* Sequence n buffer size in bytes */
+#define NRF54L_PWM_DMA_SEQ_AMOUNT_OFFSET(n) (0x070c + 36 * (n))
/* Sequence n completed transfer byte count */
+#define NRF54L_PWM_DMA_SEQ_CURRENTAMOUNT_OFFSET(n) (0x0710 + 36 * (n))
/* Sequence n current transfer byte count */
+#define NRF54L_PWM_DMA_SEQ_TERMINATEONBUSERROR_OFFSET(n) (0x071c + 36 * (n))
/* Terminate sequence n DMA on bus error */
+#define NRF54L_PWM_DMA_SEQ_BUSERRORADDRESS_OFFSET(n) (0x0720 + 36 * (n))
/* Address of last sequence n DMA bus error */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* TASKS_STOP Register */
+
+#define PWM_TASKS_STOP (1 << 0) /* Bit 0: Stop PWM */
+
+/* TASKS_DMA.SEQ[n].START Register */
+
+#define PWM_TASKS_DMA_SEQ_START (1 << 0) /* Bit 0: Start sequence */
+
+/* TASKS_NEXTSTEP Register */
+
+#define PWM_TASKS_NEXTSTEP (1 << 0) /* Bit 0: Next step */
+
+/* SHORTS Register */
+
+#define PWM_SHORTS_SEQEND0_STOP (1 << 0) /* Bit 0: Shortcut
between event SEQEND[0] and task STOP */
+#define PWM_SHORTS_SEQEND1_STOP (1 << 1) /* Bit 1: Shortcut
between event SEQEND[1] and task STOP */
+#define PWM_SHORTS_LOOPSDONE_DMA_SEQ0_START (1 << 2) /* Bit 2: Shortcut
between event LOOPSDONE and task DMA.SEQ[0].START */
+#define PWM_SHORTS_LOOPSDONE_DMA_SEQ1_START (1 << 3) /* Bit 3: Shortcut
between event LOOPSDONE and task DMA.SEQ[1].START */
+#define PWM_SHORTS_LOOPSDONE_STOP (1 << 4) /* Bit 4: Shortcut
between event LOOPSDONE and task STOP */
+#define PWM_SHORTS_RAMUNDERFLOW_STOP (1 << 5) /* Stop on RAM underflow
*/
+#define PWM_SHORTS_DMA_SEQ0_BUSERROR_STOP (1 << 6) /* Bit 6: Shortcut
between event DMA.SEQ[0].BUSERROR and task STOP */
+#define PWM_SHORTS_DMA_SEQ1_BUSERROR_STOP (1 << 7) /* Bit 7: Shortcut
between event DMA.SEQ[1].BUSERROR and task STOP */
+
+/* INTEN/INTENSET/INTENCLR Register */
+
+#define PWM_INT_STOPPED (1 << 1) /* Interrupt for
STOPPED */
+#define PWM_INT_SEQSTARTED0 (1 << 2) /* Interrupt for
SEQSTARTED0 */
+#define PWM_INT_SEQSTARTED1 (1 << 3) /* Interrupt for
SEQSTARTED1 */
+#define PWM_INT_SEQEND0 (1 << 4) /* Interrupt for
SEQEND0 */
+#define PWM_INT_SEQEND1 (1 << 5) /* Interrupt for
SEQEND1 */
+#define PWM_INT_PWMPERIODEND (1 << 6) /* Interrupt for
PWMPERIODEND */
+#define PWM_INT_LOOPSDONE (1 << 7) /* Interrupt for
LOOPSDONE */
+#define PWM_INT_RAMUNDERFLOW (1 << 8) /* Interrupt for
RAMUNDERFLOW */
+#define PWM_INT_DMA_SEQ_END(n) (1 << (9 + 3 * (n))) /* Interrupt for
sequence n DMA END */
+#define PWM_INT_DMA_SEQ_READY(n) (1 << (10 + 3 * (n))) /* Interrupt for
sequence n DMA READY */
+#define PWM_INT_DMA_SEQ_BUSERROR(n) (1 << (11 + 3 * (n))) /* Interrupt for
sequence n DMA BUSERROR */
+#define PWM_INT_COMPAREMATCH(n) (1 << (15 + (n))) /* Interrupt for
channel n COMPAREMATCH */
+
+/* ENABLE Register */
+
+#define PWM_ENABLE_ENABLE (1 << 0) /* Bit 0: Enable PWM module */
+#define PWM_ENABLE_DISABLE (0 << 0) /* Bit 0: Disable PWM module */
+
+/* MODE Register */
+
+#define PWM_MODE_UP (0 << 0) /* Bit 0: Up counter,
edge-aligned PWM */
+#define PWM_MODE_UPDOWN (1 << 0) /* Bit 0: Up and down counter,
center-aligned PWM */
+
+/* COUNTERTOP Register */
+
+#define PWM_COUNTERTOP_MASK (0x7fff)
+
+/* PRESCALER Register */
+
+#define PWM_PRESCALER_SHIFT (0)
+#define PWM_PRESCALER_MASK (7 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_16MHZ (0 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_8MHZ (1 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_4MHZ (2 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_2MHZ (3 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_1MHZ (4 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_500KHZ (5 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_250KHZ (6 << PWM_PRESCALER_SHIFT)
+# define PWM_PRESCALER_125KHZ (7 << PWM_PRESCALER_SHIFT)
+
+/* DECODER Register */
+
+#define PWM_DECODER_LOAD_SHIFT (0) /* Bits 0-1: How a sequence is read
from RAM */
+#define PWM_DECODER_LOAD_MASK (3 << PWM_DECODER_LOAD_SHIFT)
+# define PWM_DECODER_LOAD_COMMON (0 << PWM_DECODER_LOAD_SHIFT)
+# define PWM_DECODER_LOAD_GROUPED (1 << PWM_DECODER_LOAD_SHIFT)
+# define PWM_DECODER_LOAD_INDIVIDUAL (2 << PWM_DECODER_LOAD_SHIFT)
+# define PWM_DECODER_LOAD_WAVEFORM (3 << PWM_DECODER_LOAD_SHIFT)
+
+#define PWM_DECODER_MODE_REFRESH (0 << 8) /* Load using REFRESH */
+#define PWM_DECODER_MODE_NEXTSTEP (1 << 8) /* Load using NEXTSTEP */
+
+/* LOOP Register */
+
+#define PWM_LOOP_MASK (0xffff)
+
+/* DMA.SEQ[n].MAXCNT Register */
+
+#define PWM_DMA_MAXCNT_MASK (0x7fff)
+#define PWM_DMA_TERMINATEONBUSERROR (1 << 0)
+
+/* IDLEOUT Register */
+
+#define PWM_IDLEOUT_VAL(n) (1 << (n))
+
+/* SUBSCRIBE and PUBLISH Registers */
+
+#define PWM_SUBSCRIBE_CHIDX_SHIFT (0)
+#define PWM_SUBSCRIBE_CHIDX_MASK (0xff << PWM_SUBSCRIBE_CHIDX_SHIFT)
+#define PWM_SUBSCRIBE_EN (1 << 31)
+#define PWM_PUBLISH_CHIDX_SHIFT (0)
+#define PWM_PUBLISH_CHIDX_MASK (0xff << PWM_PUBLISH_CHIDX_SHIFT)
+#define PWM_PUBLISH_EN (1 << 31)
+
+/* SEQ[n]REFRESH Register */
+
+#define PWM_SEQREFRESH_MASK (0xffffff)
+
+/* SEQ[n]ENDDELAY Register */
+
+#define PWM_SEQENDDELAY_MASK (0xffffff)
+
+/* PSEL[x] Register */
+
+#define PWM_PSEL_PIN_SHIFT (0) /* Bits 0-4: OUT pin number
*/
+#define PWM_PSEL_PIN_MASK (0x1f << PWM_PSEL_PIN_SHIFT)
+#define PWM_PSEL_PORT_SHIFT (5) /* Bits 5-7: OUT port number
*/
+#define PWM_PSEL_PORT_MASK (0x7 << PWM_PSEL_PORT_SHIFT)
+#define PWM_PSEL_CONNECTED (0 << 31) /* Bit 31: Connected */
+#define PWM_PSEL_DISCONNECTED (1 << 31) /* Bit 31: Disconnected */
+#define PWM_PSEL_RESET (0xffffffff)
+
+/* Decoder data */
+
+#define PWM_DECODER_COMPARE_SHIFT (0)
+#define PWM_DECODER_COMPARE_MASK (0x7fff)
+#define PWM_DECODER_POL_RISING (0 << 15)
+#define PWM_DECODER_POL_FALLING (1 << 15)
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_PWM_H */
diff --git a/arch/arm/src/nrf54l/nrf54l_pwm.c b/arch/arm/src/nrf54l/nrf54l_pwm.c
new file mode 100644
index 00000000000..68867054daa
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_pwm.c
@@ -0,0 +1,679 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_pwm.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 <assert.h>
+#include <nuttx/debug.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "nrf54l_gpio.h"
+#include "nrf54l_pwm.h"
+
+#include "hardware/nrf54l_pwm.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Default PWM polarity */
+
+#define PWM_POLARITY_DEFAULT (PWM_DECODER_POL_FALLING)
+
+/* Sequence 0 length */
+
+#define PWM_SEQ0_LEN (4)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nrf54l_pwm_s
+{
+ const struct pwm_ops_s *ops; /* PWM operations */
+ uint32_t base; /* Base address of PWM register */
+ uint32_t frequency; /* Current frequency setting */
+ uint32_t cntrtop; /* Counter top */
+ uint32_t ch0_pin; /* Channel 1 pin */
+ uint32_t ch1_pin; /* Channel 2 pin */
+ uint32_t ch2_pin; /* Channel 3 pin */
+ uint32_t ch3_pin; /* Channel 4 pin */
+
+ /* Sequence 0 */
+
+ uint16_t seq0[PWM_SEQ0_LEN];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* PWM Register access */
+
+static inline void nrf54l_pwm_putreg(struct nrf54l_pwm_s *priv,
+ uint32_t offset,
+ uint32_t value);
+static inline uint32_t nrf54l_pwm_getreg(struct nrf54l_pwm_s *priv,
+ uint32_t offset);
+
+/* PWM helpers */
+
+static int nrf54l_pwm_configure(struct nrf54l_pwm_s *priv);
+static int nrf54l_pwm_duty(struct nrf54l_pwm_s *priv, uint8_t chan,
+ ub16_t duty);
+static int nrf54l_pwm_freq(struct nrf54l_pwm_s *priv, uint32_t freq);
+
+/* PWM driver methods */
+
+static int nrf54l_pwm_setup(struct pwm_lowerhalf_s *dev);
+static int nrf54l_pwm_shutdown(struct pwm_lowerhalf_s *dev);
+static int nrf54l_pwm_start(struct pwm_lowerhalf_s *dev,
+ const struct pwm_info_s *info);
+static int nrf54l_pwm_stop(struct pwm_lowerhalf_s *dev);
+static int nrf54l_pwm_ioctl(struct pwm_lowerhalf_s *dev,
+ int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is the list of lower half PWM driver methods used by the upper half
+ * driver.
+ */
+
+static const struct pwm_ops_s g_nrf54l_pwmops =
+{
+ .setup = nrf54l_pwm_setup,
+ .shutdown = nrf54l_pwm_shutdown,
+ .start = nrf54l_pwm_start,
+ .stop = nrf54l_pwm_stop,
+ .ioctl = nrf54l_pwm_ioctl,
+};
+
+#ifdef CONFIG_NRF54L_PWM0
+/* PWM 0 */
+
+struct nrf54l_pwm_s g_nrf54l_pwm0 =
+{
+ .ops = &g_nrf54l_pwmops,
+ .base = NRF54L_PWM20_BASE,
+#ifdef CONFIG_NRF54L_PWM0_CH0
+ .ch0_pin = NRF54L_PWM0_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM0_CH1
+ .ch1_pin = NRF54L_PWM0_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM0_CH2
+ .ch2_pin = NRF54L_PWM0_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM0_CH3
+ .ch3_pin = NRF54L_PWM0_CH3_PIN,
+#endif
+};
+#endif
+
+#ifdef CONFIG_NRF54L_PWM1
+/* PWM 1 */
+
+struct nrf54l_pwm_s g_nrf54l_pwm1 =
+{
+ .ops = &g_nrf54l_pwmops,
+ .base = NRF54L_PWM21_BASE,
+#ifdef CONFIG_NRF54L_PWM1_CH0
+ .ch0_pin = NRF54L_PWM1_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM1_CH1
+ .ch1_pin = NRF54L_PWM1_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM1_CH2
+ .ch2_pin = NRF54L_PWM1_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM1_CH3
+ .ch3_pin = NRF54L_PWM1_CH3_PIN,
+#endif
+};
+#endif
+
+#ifdef CONFIG_NRF54L_PWM2
+/* PWM 2 */
+
+struct nrf54l_pwm_s g_nrf54l_pwm2 =
+{
+ .ops = &g_nrf54l_pwmops,
+ .base = NRF54L_PWM22_BASE,
+#ifdef CONFIG_NRF54L_PWM2_CH0
+ .ch0_pin = NRF54L_PWM2_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM2_CH1
+ .ch1_pin = NRF54L_PWM2_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM2_CH2
+ .ch2_pin = NRF54L_PWM2_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF54L_PWM2_CH3
+ .ch3_pin = NRF54L_PWM2_CH3_PIN,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_pwm_putreg
+ *
+ * Description:
+ * Put a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void nrf54l_pwm_putreg(struct nrf54l_pwm_s *priv,
+ uint32_t offset,
+ uint32_t value)
+{
+ putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_getreg
+ *
+ * Description:
+ * Get a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint32_t nrf54l_pwm_getreg(struct nrf54l_pwm_s *priv,
+ uint32_t offset)
+{
+ return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_configure
+ *
+ * Description:
+ * Configure PWM
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_configure(struct nrf54l_pwm_s *priv)
+{
+ uint32_t regval = 0;
+ int ret = OK;
+
+ DEBUGASSERT(priv);
+
+ /* Configure PWM mode */
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_MODE_OFFSET, PWM_MODE_UP);
+
+ /* Configure decoder */
+
+ regval = PWM_DECODER_LOAD_INDIVIDUAL | PWM_DECODER_MODE_REFRESH;
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_DECODER_OFFSET, regval);
+
+ /* Configure sequence 0 */
+
+ regval = (uintptr_t)priv->seq0;
+ DEBUGASSERT((regval & 3) == 0);
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_DMA_SEQ_PTR_OFFSET(0), regval);
+
+ /* Sequence length is in bytes */
+
+ regval = sizeof(priv->seq0);
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_DMA_SEQ_MAXCNT_OFFSET(0), regval);
+
+ regval = 0;
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_SEQ0REFRESH_OFFSET, regval);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_duty
+ *
+ * Description:
+ * Configure PWM duty
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_duty(struct nrf54l_pwm_s *priv, uint8_t chan,
+ ub16_t duty)
+{
+ uint16_t compare = 0;
+
+ DEBUGASSERT(priv);
+
+ pwminfo("PWM channel: %d duty: %" PRId32 "\n", chan, duty);
+
+ /* Get compare
+ *
+ * duty cycle = compare / reload (fractional value)
+ */
+
+ compare = b16toi(duty * priv->cntrtop + b16HALF);
+
+ /* Configure channel sequence */
+
+ priv->seq0[chan] = PWM_POLARITY_DEFAULT | compare;
+
+ pwminfo("seq0[%d]: %d %d\n", chan, compare, priv->seq0[chan]);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_freq
+ *
+ * Description:
+ * Configure PWM frequency
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_freq(struct nrf54l_pwm_s *priv, uint32_t freq)
+{
+ uint32_t regval = 0;
+ uint32_t pwm_clk = 0;
+ uint32_t top = 0;
+ uint32_t prescaler = 0;
+ uint64_t tmp = 0;
+ int ret = OK;
+
+ DEBUGASSERT(priv);
+
+ /* Get best prescaler */
+
+ tmp = PWM_COUNTERTOP_MASK * freq;
+
+ if (tmp >= 16000000)
+ {
+ pwm_clk = 16000000;
+ prescaler = PWM_PRESCALER_16MHZ;
+ }
+ else if (tmp >= 8000000)
+ {
+ pwm_clk = 8000000;
+ prescaler = PWM_PRESCALER_8MHZ;
+ }
+ else if (tmp >= 4000000)
+ {
+ pwm_clk = 4000000;
+ prescaler = PWM_PRESCALER_4MHZ;
+ }
+ else if (tmp >= 2000000)
+ {
+ pwm_clk = 2000000;
+ prescaler = PWM_PRESCALER_2MHZ;
+ }
+ else if (tmp >= 1000000)
+ {
+ pwm_clk = 1000000;
+ prescaler = PWM_PRESCALER_1MHZ;
+ }
+ else if (tmp >= 500000)
+ {
+ pwm_clk = 500000;
+ prescaler = PWM_PRESCALER_500KHZ;
+ }
+ else if (tmp >= 250000)
+ {
+ pwm_clk = 250000;
+ prescaler = PWM_PRESCALER_250KHZ;
+ }
+ else
+ {
+ pwm_clk = 125000;
+ prescaler = PWM_PRESCALER_125KHZ;
+ }
+
+ /* Configure prescaler */
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_PRESCALER_OFFSET, prescaler);
+
+ /* Get counter max */
+
+ top = pwm_clk / freq;
+ if (top < 2)
+ {
+ top = 1;
+ }
+ else if (top > PWM_COUNTERTOP_MASK)
+ {
+ top = PWM_COUNTERTOP_MASK;
+ }
+ else
+ {
+ top = top - 1;
+ }
+
+ /* Configure counter max */
+
+ regval = top;
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_COUNTERTOP_OFFSET, regval);
+
+ priv->cntrtop = top;
+
+ pwminfo("PWM frequency: %" PRId32 " pwm_clk: %" PRId32
+ " pwm_prescaler: %" PRId32 " top: %" PRId32 "\n",
+ freq, pwm_clk, prescaler, top);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_setup
+ *
+ * Description:
+ * This method is called when the driver is opened. The lower half driver
+ * should configure and initialize the device so that it is ready for use.
+ * It should not, however, output pulses until the start method is called.
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_setup(struct pwm_lowerhalf_s *dev)
+{
+ struct nrf54l_pwm_s *priv = (struct nrf54l_pwm_s *)dev;
+ int ret = OK;
+ uint32_t regval = 0;
+ uint32_t pin = 0;
+ uint32_t port = 0;
+
+ DEBUGASSERT(dev);
+
+ /* Configure channels */
+
+ if (priv->ch0_pin != 0)
+ {
+ nrf54l_gpio_config(priv->ch0_pin);
+
+ pin = GPIO_PIN_DECODE(priv->ch0_pin);
+ port = GPIO_PORT_DECODE(priv->ch0_pin);
+
+ regval = (port << PWM_PSEL_PORT_SHIFT);
+ regval |= (pin << PWM_PSEL_PIN_SHIFT);
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_PSEL0_OFFSET, regval);
+ }
+
+ if (priv->ch1_pin != 0)
+ {
+ nrf54l_gpio_config(priv->ch1_pin);
+
+ pin = GPIO_PIN_DECODE(priv->ch1_pin);
+ port = GPIO_PORT_DECODE(priv->ch1_pin);
+
+ regval = (port << PWM_PSEL_PORT_SHIFT);
+ regval |= (pin << PWM_PSEL_PIN_SHIFT);
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_PSEL1_OFFSET, regval);
+ }
+
+ if (priv->ch2_pin != 0)
+ {
+ nrf54l_gpio_config(priv->ch2_pin);
+
+ pin = GPIO_PIN_DECODE(priv->ch2_pin);
+ port = GPIO_PORT_DECODE(priv->ch2_pin);
+
+ regval = (port << PWM_PSEL_PORT_SHIFT);
+ regval |= (pin << PWM_PSEL_PIN_SHIFT);
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_PSEL2_OFFSET, regval);
+ }
+
+ if (priv->ch3_pin != 0)
+ {
+ nrf54l_gpio_config(priv->ch3_pin);
+
+ pin = GPIO_PIN_DECODE(priv->ch3_pin);
+ port = GPIO_PORT_DECODE(priv->ch3_pin);
+
+ regval = (port << PWM_PSEL_PORT_SHIFT);
+ regval |= (pin << PWM_PSEL_PIN_SHIFT);
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_PSEL3_OFFSET, regval);
+ }
+
+ /* Configure PWM */
+
+ ret = nrf54l_pwm_configure(priv);
+ if (ret < 0)
+ {
+ pwmerr("ERROR: nrf54l_pwm_configure failed %d\n", ret);
+ goto errout;
+ }
+
+ /* Enable PWM */
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_ENABLE_OFFSET, 1);
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_shutdown
+ *
+ * Description:
+ * This method is called when the driver is closed. The lower half driver
+ * stop pulsed output, free any resources, disable the timer hardware, and
+ * put the system into the lowest possible power usage state
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_shutdown(struct pwm_lowerhalf_s *dev)
+{
+ struct nrf54l_pwm_s *priv = (struct nrf54l_pwm_s *)dev;
+ int ret = OK;
+
+ DEBUGASSERT(dev);
+
+ /* Disable PWM */
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_ENABLE_OFFSET, 0);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_start
+ *
+ * Description:
+ * (Re-)initialize the PWM and start the pulsed output
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_start(struct pwm_lowerhalf_s *dev,
+ const struct pwm_info_s *info)
+{
+ struct nrf54l_pwm_s *priv = (struct nrf54l_pwm_s *)dev;
+ int ret = OK;
+ int i = 0;
+
+ DEBUGASSERT(dev);
+
+ /* If frequency has not changed we just update duty */
+
+ if (info->frequency != priv->frequency)
+ {
+ /* Update frequency */
+
+ ret = nrf54l_pwm_freq(priv, info->frequency);
+
+ if (ret == OK)
+ {
+ priv->frequency = info->frequency;
+ }
+ }
+
+ for (i = 0; ret == OK && i < CONFIG_PWM_NCHANNELS; i++)
+ {
+ /* Break the loop if all following channels are not configured */
+
+ if (info->channels[i].channel == -1)
+ {
+ break;
+ }
+
+ /* Set output if channel configured */
+
+ if (info->channels[i].channel != 0)
+ {
+ ret = nrf54l_pwm_duty(priv,
+ (info->channels[i].channel - 1),
+ info->channels[i].duty);
+ }
+ }
+
+ /* Start sequence 0 */
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_EVENTS_SEQSTARTED0_OFFSET, 0);
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_TASKS_DMA_SEQ_START_OFFSET(0), 1);
+
+ /* Wait for sequence started */
+
+ while (nrf54l_pwm_getreg(priv, NRF54L_PWM_EVENTS_SEQSTARTED0_OFFSET) != 1);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_stop
+ *
+ * Description:
+ * Stop the PWM
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_stop(struct pwm_lowerhalf_s *dev)
+{
+ struct nrf54l_pwm_s *priv = (struct nrf54l_pwm_s *)dev;
+
+ DEBUGASSERT(dev);
+
+ /* Stop PWM */
+
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_EVENTS_STOPPED_OFFSET, 0);
+ nrf54l_pwm_putreg(priv, NRF54L_PWM_TASKS_STOP_OFFSET, 1);
+
+ /* Wait for PWM stopped */
+
+ while (nrf54l_pwm_getreg(priv, NRF54L_PWM_EVENTS_STOPPED_OFFSET) != 1);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nrf54l_pwm_ioctl
+ *
+ * Description:
+ * Lower-half logic may support platform-specific ioctl commands
+ *
+ ****************************************************************************/
+
+static int nrf54l_pwm_ioctl(struct pwm_lowerhalf_s *dev,
+ int cmd, unsigned long arg)
+{
+ struct nrf54l_pwm_s *priv = (struct nrf54l_pwm_s *)dev;
+
+ DEBUGASSERT(dev);
+
+ /* There are no platform-specific ioctl commands */
+
+ UNUSED(priv);
+
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Function
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_pwminitialize
+ *
+ * Description:
+ * Initialize one timer for use with the upper_level PWM driver.
+ *
+ * Input Parameters:
+ * pwm - A number identifying the pwm instance.
+ *
+ * Returned Value:
+ * On success, a pointer to the NRF54L lower half PWM driver is returned.
+ * NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+struct pwm_lowerhalf_s *nrf54l_pwminitialize(int pwm)
+{
+ struct nrf54l_pwm_s *lower = NULL;
+
+ pwminfo("Initialize PWM%u\n", pwm);
+
+ switch (pwm)
+ {
+#ifdef CONFIG_NRF54L_PWM0
+ case 0:
+ {
+ lower = &g_nrf54l_pwm0;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_PWM1
+ case 1:
+ {
+ lower = &g_nrf54l_pwm1;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_NRF54L_PWM2
+ case 2:
+ {
+ lower = &g_nrf54l_pwm2;
+ break;
+ }
+#endif
+
+ default:
+ {
+ pwmerr("ERROR: No such PWM device %d\n", pwm);
+ lower = NULL;
+ goto errout;
+ }
+ }
+
+errout:
+ return (struct pwm_lowerhalf_s *)lower;
+}
diff --git a/arch/arm/src/nrf54l/nrf54l_pwm.h b/arch/arm/src/nrf54l/nrf54l_pwm.h
new file mode 100644
index 00000000000..ae1a1297850
--- /dev/null
+++ b/arch/arm/src/nrf54l/nrf54l_pwm.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/nrf54l_pwm.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_PWM_H
+#define __ARCH_ARM_SRC_NRF54L_NRF54L_PWM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/timers/pwm.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Enable the specified PWM channel if multichannel PWM is disabled */
+
+#ifndef CONFIG_NRF54L_PWM_MULTICHAN
+
+# ifdef CONFIG_NRF54L_PWM0
+# if !defined(CONFIG_NRF54L_PWM0_CHANNEL)
+# error "CONFIG_NRF54L_PWM0_CHANNEL must be provided"
+# elif CONFIG_NRF54L_PWM0_CHANNEL == 0
+# define CONFIG_NRF54L_PWM0_CH0 1
+# elif CONFIG_NRF54L_PWM0_CHANNEL == 1
+# define CONFIG_NRF54L_PWM0_CH1 1
+# elif CONFIG_NRF54L_PWM0_CHANNEL == 2
+# define CONFIG_NRF54L_PWM0_CH2 1
+# elif CONFIG_NRF54L_PWM0_CHANNEL == 3
+# define CONFIG_NRF54L_PWM0_CH3 1
+# else
+# error "Unsupported value of CONFIG_NRF54L_PWM0_CHANNEL"
+# endif
+# endif
+
+# ifdef CONFIG_NRF54L_PWM1
+# if !defined(CONFIG_NRF54L_PWM1_CHANNEL)
+# error "CONFIG_NRF54L_PWM1_CHANNEL must be provided"
+# elif CONFIG_NRF54L_PWM1_CHANNEL == 0
+# define CONFIG_NRF54L_PWM1_CH0 1
+# elif CONFIG_NRF54L_PWM1_CHANNEL == 1
+# define CONFIG_NRF54L_PWM1_CH1 1
+# elif CONFIG_NRF54L_PWM1_CHANNEL == 2
+# define CONFIG_NRF54L_PWM1_CH2 1
+# elif CONFIG_NRF54L_PWM1_CHANNEL == 3
+# define CONFIG_NRF54L_PWM1_CH3 1
+# else
+# error "Unsupported value of CONFIG_NRF54L_PWM1_CHANNEL"
+# endif
+# endif
+
+# ifdef CONFIG_NRF54L_PWM2
+# if !defined(CONFIG_NRF54L_PWM2_CHANNEL)
+# error "CONFIG_NRF54L_PWM2_CHANNEL must be provided"
+# elif CONFIG_NRF54L_PWM2_CHANNEL == 0
+# define CONFIG_NRF54L_PWM2_CH0 1
+# elif CONFIG_NRF54L_PWM2_CHANNEL == 1
+# define CONFIG_NRF54L_PWM2_CH1 1
+# elif CONFIG_NRF54L_PWM2_CHANNEL == 2
+# define CONFIG_NRF54L_PWM2_CH2 1
+# elif CONFIG_NRF54L_PWM2_CHANNEL == 3
+# define CONFIG_NRF54L_PWM2_CH3 1
+# else
+# error "Unsupported value of CONFIG_NRF54L_PWM2_CHANNEL"
+# endif
+# endif
+
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf54l_pwminitialize
+ *
+ * Description:
+ * Initialize one timer for use with the upper_level PWM driver.
+ *
+ * Input Parameters:
+ * pwm - A number identifying the pwm instance.
+ *
+ * Returned Value:
+ * On success, a pointer to the NRF54L lower half PWM driver is returned.
+ * NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+struct pwm_lowerhalf_s *nrf54l_pwminitialize(int pwm);
+
+#endif /* __ARCH_ARM_SRC_NRF54L_NRF54L_PWM_H */