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 bb025be12db arch/nrf91: add PWM support
bb025be12db is described below

commit bb025be12db86bad3e1a7b931926bf646e2ed5e5
Author: raiden00pl <[email protected]>
AuthorDate: Thu Jun 11 09:19:39 2026 +0200

    arch/nrf91: add PWM support
    
    add PWM support for nrf91
    
    Signed-off-by: raiden00pl <[email protected]>
---
 arch/arm/src/nrf91/Kconfig              |  39 ++
 arch/arm/src/nrf91/hardware/nrf91_pwm.h | 180 ++++++++
 arch/arm/src/nrf91/nrf91_pwm.c          | 706 ++++++++++++++++++++++++++++++++
 arch/arm/src/nrf91/nrf91_pwm.h          | 133 ++++++
 4 files changed, 1058 insertions(+)

diff --git a/arch/arm/src/nrf91/Kconfig b/arch/arm/src/nrf91/Kconfig
index 57048b5fb8c..120688899fd 100644
--- a/arch/arm/src/nrf91/Kconfig
+++ b/arch/arm/src/nrf91/Kconfig
@@ -203,6 +203,11 @@ config NRF91_PWM2
        select NRF91_PWM
        default n
 
+config NRF91_PWM3
+       bool "PWM3"
+       select NRF91_PWM
+       default n
+
 config NRF91_SAADC
        bool "SAADC"
        default n
@@ -568,6 +573,34 @@ config NRF91_PWM2_CH3
 
 endif # NRF91_PWM2
 
+if NRF91_PWM3
+
+config NRF91_PWM3_CH0
+       bool "PWM3 Channel 0 Output"
+       default n
+       ---help---
+               Enables channel 0 output.
+
+config NRF91_PWM3_CH1
+       bool "PWM3 Channel 1 Output"
+       default n
+       ---help---
+               Enables channel 1 output.
+
+config NRF91_PWM3_CH2
+       bool "PWM3 Channel 2 Output"
+       default n
+       ---help---
+               Enables channel 2 output.
+
+config NRF91_PWM3_CH3
+       bool "PWM3 Channel 3 Output"
+       default n
+       ---help---
+               Enables channel 3 output.
+
+endif # NRF91_PWM3
+
 endif # !NRF91_PWM_MULTICHAN
 
 if !NRF91_PWM_MULTICHAN
@@ -590,6 +623,12 @@ config NRF91_PWM2_CHANNEL
        default 0
        range 0 3
 
+config NRF91_PWM3_CHANNEL
+       int "PWM3 Output Channel"
+       depends on NRF91_PWM3
+       default 0
+       range 0 3
+
 endif # !NRF91_PWM_MULTICHAN
 
 endif # NRF91_PWM
diff --git a/arch/arm/src/nrf91/hardware/nrf91_pwm.h 
b/arch/arm/src/nrf91/hardware/nrf91_pwm.h
new file mode 100644
index 00000000000..95249fc1a1c
--- /dev/null
+++ b/arch/arm/src/nrf91/hardware/nrf91_pwm.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+ * arch/arm/src/nrf91/hardware/nrf91_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_NRF91_HARDWARE_NRF91_PWM_H
+#define __ARCH_ARM_SRC_NRF91_HARDWARE_NRF91_PWM_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf91_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* Register offsets *********************************************************/
+
+#define NRF91_PWM_TASKS_STOP_OFFSET          0x0004 /* Stop PWM */
+#define NRF91_PWM_TASKS_SEQSTART0_OFFSET     0x0008 /* Sequence 0 start */
+#define NRF91_PWM_TASKS_SEQSTART1_OFFSET     0x000c /* Sequence 1 start  */
+#define NRF91_PWM_TASKS_NEXTSTEP_OFFSET      0x0010 /* Steps by one value in 
the current sequence */
+                                                    /* TODO: 0x084-0x090 */
+#define NRF91_PWM_EVENTS_STOPPED_OFFSET      0x0104 /* STOP event */
+#define NRF91_PWM_EVENTS_SEQSTARTED0_OFFSET  0x0108 /* Sequence 0 started 
event */
+#define NRF91_PWM_EVENTS_SEQSTARTED1_OFFSET  0x010c /* Sequence 1 started 
event */
+#define NRF91_PWM_EVENTS_SEQEND0_OFFSET      0x0110 /* Sequence 0 end event */
+#define NRF91_PWM_EVENTS_SEQEND1_OFFSET      0x0114 /* Sequence 1 end event */
+#define NRF91_PWM_EVENTS_PWMPERIODEN_OFFSET  0x0118 /* PWM period end event */
+#define NRF91_PWM_EVENTS_LOOPSDONE_OFFSET    0x011c /* Loop done event */
+                                                    /* TODO: 0x184-0x19c */
+#define NRF91_PWM_SHORTS_OFFSET              0x0200 /* Shourtcut register */
+#define NRF91_PWM_INTEN_OFFSET               0x0300 /* Enable or disable 
interrupt */
+#define NRF91_PWM_INTENSET_OFFSET            0x0304 /* Enable interrupt */
+#define NRF91_PWM_INTENCLR_OFFSET            0x0308 /* Disable interrupt */
+#define NRF91_PWM_ENABLE_OFFSET              0x0500 /* PWM enable */
+#define NRF91_PWM_MODE_OFFSET                0x0504 /* Wave counter mode */
+#define NRF91_PWM_COUNTERTOP_OFFSET          0x0508 /* Counter max value */
+#define NRF91_PWM_PRESCALER_OFFSET           0x050c /* Prescaler configuration 
*/
+#define NRF91_PWM_DECODER_OFFSET             0x0510 /* Configuration of the 
decoder */
+#define NRF91_PWM_LOOP_OFFSET                0x0514 /* Amount of playback of a 
loop */
+#define NRF91_PWM_SEQ0PTR_OFFSET             0x0520 /* Sequence 0 beginning 
address */
+#define NRF91_PWM_SEQ0CNT_OFFSET             0x0524 /* Sequence 0 length */
+#define NRF91_PWM_SEQ0REFRESH_OFFSET         0x0528 /* Sequence 0 additional 
periods */
+#define NRF91_PWM_SEQ0ENDDELAY_OFFSET        0x052c /* Time added after 
sequence 0 */
+#define NRF91_PWM_SEQ1PTR_OFFSET             0x0540 /* Sequence 1 beginning 
address */
+#define NRF91_PWM_SEQ1CNT_OFFSET             0x0544 /* Sequence 1 length */
+#define NRF91_PWM_SEQ1REFRESH_OFFSET         0x0548 /* Sequence 0 additional 
periods */
+#define NRF91_PWM_SEQ1ENDDELAY_OFFSET        0x054c /* Time added after 
sequence 1 */
+#define NRF91_PWM_PSEL0_OFFSET               0x0560 /* Output pin select for 
PWM chan 0 */
+#define NRF91_PWM_PSEL1_OFFSET               0x0564 /* Output pin select for 
PWM chan 1 */
+#define NRF91_PWM_PSEL2_OFFSET               0x0568 /* Output pin select for 
PWM chan 2 */
+#define NRF91_PWM_PSEL3_OFFSET               0x056c /* Output pin select for 
PWM chan 3 */
+
+/* Register Bitfield Definitions ********************************************/
+
+/* TASKS_STOP Register */
+
+#define PWM_TASKS_STOP                 (1 << 0) /* Bit 0: Stop PWM */
+
+/* TASKS_SEQSTART[n] Register */
+
+#define PWM_TASKS_SEQSTART             (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_SEQSTART0 (1 << 2) /* Bit 2: Shortcut between 
event LOOPSDONE and task SEQSTART[0] */
+#define PWM_SHORTS_LOOPSDONE_SEQSTART1 (1 << 3) /* Bit 3: Shortcut between 
event LOOPSDONE and task SEQSTART[1] */
+#define PWM_SHORTS_LOOPSDONE_STOP      (1 << 4) /* Bit 4: Shortcut between 
event LOOPSDONE and task STOP */
+
+/* INTEN/INTENSET/INTENCLR Register */
+
+#define PWM_INT_STOPPED                (1 << 0) /* Bit 0: Interrupt for event 
STOPPED */
+#define PWM_INT_SEQSTARTED0            (1 << 1) /* Bit 1: Interrupt for event 
SEQSTARTED0 */
+#define PWM_INT_SEQSTARTED1            (1 << 2) /* Bit 2: Interrupt for event 
SEQSTARTED2 */
+#define PWM_INT_SEQEND0                (1 << 3) /* Bit 3: Interrupt for event 
SEQEND0 */
+#define PWM_INT_SEQEND1                (1 << 4) /* Bit 4: Interrupt for event 
SEQEND1 */
+#define PWM_INT_PWMPERIODEND           (1 << 5) /* Bit 5: Interrupt for event 
PWMPERIODEND */
+#define PWM_INT_LOOPSDONE              (1 << 6) /* Bit 6: Interrupt for event 
LOOPSDONE */
+
+/* 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       (8 << 0) /* Bit 8: */
+#define PWM_DECODER_MODE_NEXTSTEP      (8 << 1) /* Bit 8: */
+
+/* LOOP Register */
+
+#define PWM_LOOP_MASK                  (0xffff)
+
+/* SEQ[n]CNT Register */
+
+#define PWM_SEQCNT_MASK                (0x7fff)
+
+/* 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_PSELSDA_PIN_SHIFT)
+#define PWM_PSEL_PORT_SHIFT            (5)        /* Bit 5: PUT port number */
+#define PWM_PSEL_PORT_MASK             (0x1 << PWM_PSELSDA_PORT_SHIFT)
+#define PWM_PSEL_CONNECTED             (1 << 31)  /* Bit 31: Connection */
+#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_NRF91_HARDWARE_NRF91_PWM_H */
diff --git a/arch/arm/src/nrf91/nrf91_pwm.c b/arch/arm/src/nrf91/nrf91_pwm.c
new file mode 100644
index 00000000000..964ac96ef51
--- /dev/null
+++ b/arch/arm/src/nrf91/nrf91_pwm.c
@@ -0,0 +1,706 @@
+/****************************************************************************
+ * arch/arm/src/nrf91/nrf91_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 "nrf91_gpio.h"
+#include "nrf91_pwm.h"
+
+#include "hardware/nrf91_pwm.h"
+#include "hardware/nrf91_utils.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 nrf91_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 nrf91_pwm_putreg(struct nrf91_pwm_s *priv,
+                                    uint32_t offset,
+                                    uint32_t value);
+static inline uint32_t nrf91_pwm_getreg(struct nrf91_pwm_s *priv,
+                                        uint32_t offset);
+
+/* PWM helpers */
+
+static int nrf91_pwm_configure(struct nrf91_pwm_s *priv);
+static int nrf91_pwm_duty(struct nrf91_pwm_s *priv, uint8_t chan,
+                          ub16_t duty);
+static int nrf91_pwm_freq(struct nrf91_pwm_s *priv, uint32_t freq);
+
+/* PWM driver methods */
+
+static int nrf91_pwm_setup(struct pwm_lowerhalf_s *dev);
+static int nrf91_pwm_shutdown(struct pwm_lowerhalf_s *dev);
+static int nrf91_pwm_start(struct pwm_lowerhalf_s *dev,
+                           const struct pwm_info_s *info);
+static int nrf91_pwm_stop(struct pwm_lowerhalf_s *dev);
+static int nrf91_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_nrf91_pwmops =
+{
+  .setup       = nrf91_pwm_setup,
+  .shutdown    = nrf91_pwm_shutdown,
+  .start       = nrf91_pwm_start,
+  .stop        = nrf91_pwm_stop,
+  .ioctl       = nrf91_pwm_ioctl,
+};
+
+#ifdef CONFIG_NRF91_PWM0
+/* PWM 0 */
+
+struct nrf91_pwm_s g_nrf91_pwm0 =
+{
+  .ops     = &g_nrf91_pwmops,
+  .base    = NRF91_PWM0_BASE,
+#ifdef CONFIG_NRF91_PWM0_CH0
+  .ch0_pin = NRF91_PWM0_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM0_CH1
+  .ch1_pin = NRF91_PWM0_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM0_CH2
+  .ch2_pin = NRF91_PWM0_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM0_CH3
+  .ch3_pin = NRF91_PWM0_CH3_PIN,
+#endif
+};
+#endif
+
+#ifdef CONFIG_NRF91_PWM1
+/* PWM 1 */
+
+struct nrf91_pwm_s g_nrf91_pwm1 =
+{
+  .ops     = &g_nrf91_pwmops,
+  .base    = NRF91_PWM1_BASE,
+#ifdef CONFIG_NRF91_PWM1_CH0
+  .ch0_pin = NRF91_PWM1_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM1_CH1
+  .ch1_pin = NRF91_PWM1_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM1_CH2
+  .ch2_pin = NRF91_PWM1_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM1_CH3
+  .ch3_pin = NRF91_PWM1_CH3_PIN,
+#endif
+};
+#endif
+
+#ifdef CONFIG_NRF91_PWM2
+/* PWM 2 */
+
+struct nrf91_pwm_s g_nrf91_pwm2 =
+{
+  .ops     = &g_nrf91_pwmops,
+  .base    = NRF91_PWM2_BASE,
+#ifdef CONFIG_NRF91_PWM2_CH0
+  .ch0_pin = NRF91_PWM2_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM2_CH1
+  .ch1_pin = NRF91_PWM2_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM2_CH2
+  .ch2_pin = NRF91_PWM2_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM2_CH3
+  .ch3_pin = NRF91_PWM2_CH3_PIN,
+#endif
+};
+#endif
+
+#ifdef CONFIG_NRF91_PWM3
+/* PWM 2 */
+
+struct nrf91_pwm_s g_nrf91_pwm3 =
+{
+  .ops     = &g_nrf91_pwmops,
+  .base    = NRF91_PWM3_BASE,
+#ifdef CONFIG_NRF91_PWM3_CH0
+  .ch0_pin = NRF91_PWM3_CH0_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM3_CH1
+  .ch1_pin = NRF91_PWM3_CH1_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM3_CH2
+  .ch2_pin = NRF91_PWM3_CH2_PIN,
+#endif
+#ifdef CONFIG_NRF91_PWM3_CH3
+  .ch3_pin = NRF91_PWM3_CH3_PIN,
+#endif
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf91_pwm_putreg
+ *
+ * Description:
+ *   Put a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline void nrf91_pwm_putreg(struct nrf91_pwm_s *priv,
+                                    uint32_t offset,
+                                    uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf91_pwm_getreg
+ *
+ * Description:
+ *   Get a 32-bit register value by offset
+ *
+ ****************************************************************************/
+
+static inline uint32_t nrf91_pwm_getreg(struct nrf91_pwm_s *priv,
+                                        uint32_t offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+/****************************************************************************
+ * Name: nrf91_pwm_configure
+ *
+ * Description:
+ *   Configure PWM
+ *
+ ****************************************************************************/
+
+static int nrf91_pwm_configure(struct nrf91_pwm_s *priv)
+{
+  uint32_t regval = 0;
+  int      ret    = OK;
+
+  DEBUGASSERT(priv);
+
+  /* Configure PWM mode */
+
+  nrf91_pwm_putreg(priv, NRF91_PWM_MODE_OFFSET, PWM_MODE_UP);
+
+  /* Configure decoder  */
+
+  regval = PWM_DECODER_LOAD_INDIVIDUAL | PWM_DECODER_MODE_REFRESH;
+  nrf91_pwm_putreg(priv, NRF91_PWM_DECODER_OFFSET, regval);
+
+  /* Configure sequence 0 */
+
+  regval = (uint32_t)priv->seq0;
+  DEBUGASSERT(nrf91_easydma_valid(regval));
+  nrf91_pwm_putreg(priv, NRF91_PWM_SEQ0PTR_OFFSET, regval);
+
+  regval = PWM_SEQ0_LEN;
+  nrf91_pwm_putreg(priv, NRF91_PWM_SEQ0CNT_OFFSET, regval);
+
+  regval = 0;
+  nrf91_pwm_putreg(priv, NRF91_PWM_SEQ0REFRESH_OFFSET, regval);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf91_pwm_duty
+ *
+ * Description:
+ *   Configure PWM duty
+ *
+ ****************************************************************************/
+
+static int nrf91_pwm_duty(struct nrf91_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: nrf91_pwm_freq
+ *
+ * Description:
+ *   Configure PWM frequency
+ *
+ ****************************************************************************/
+
+static int nrf91_pwm_freq(struct nrf91_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 */
+
+  nrf91_pwm_putreg(priv, NRF91_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;
+  nrf91_pwm_putreg(priv, NRF91_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: nrf91_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 nrf91_pwm_setup(struct pwm_lowerhalf_s *dev)
+{
+  struct nrf91_pwm_s *priv = (struct nrf91_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)
+    {
+      nrf91_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);
+
+      nrf91_pwm_putreg(priv, NRF91_PWM_PSEL0_OFFSET, regval);
+    }
+
+  if (priv->ch1_pin != 0)
+    {
+      nrf91_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);
+
+      nrf91_pwm_putreg(priv, NRF91_PWM_PSEL1_OFFSET, regval);
+    }
+
+  if (priv->ch2_pin != 0)
+    {
+      nrf91_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);
+
+      nrf91_pwm_putreg(priv, NRF91_PWM_PSEL2_OFFSET, regval);
+    }
+
+  if (priv->ch3_pin != 0)
+    {
+      nrf91_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);
+
+      nrf91_pwm_putreg(priv, NRF91_PWM_PSEL3_OFFSET, regval);
+    }
+
+  /* Configure PWM */
+
+  ret = nrf91_pwm_configure(priv);
+  if (ret < 0)
+    {
+      pwmerr("ERROR: nrf91_pwm_configure failed %d\n", ret);
+      goto errout;
+    }
+
+  /* Enable PWM */
+
+  nrf91_pwm_putreg(priv, NRF91_PWM_ENABLE_OFFSET, 1);
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf91_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 nrf91_pwm_shutdown(struct pwm_lowerhalf_s *dev)
+{
+  struct nrf91_pwm_s *priv = (struct nrf91_pwm_s *)dev;
+  int                 ret  = OK;
+
+  DEBUGASSERT(dev);
+
+  /* Disable PWM */
+
+  nrf91_pwm_putreg(priv, NRF91_PWM_ENABLE_OFFSET, 0);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf91_pwm_start
+ *
+ * Description:
+ *   (Re-)initialize the PWM and start the pulsed output
+ *
+ ****************************************************************************/
+
+static int nrf91_pwm_start(struct pwm_lowerhalf_s *dev,
+                           const struct pwm_info_s *info)
+{
+  struct nrf91_pwm_s *priv = (struct nrf91_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 = nrf91_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 = nrf91_pwm_duty(priv,
+                               (info->channels[i].channel - 1),
+                               info->channels[i].duty);
+        }
+    }
+
+  /* Start sequence 0 */
+
+  nrf91_pwm_putreg(priv, NRF91_PWM_TASKS_SEQSTART0_OFFSET, 1);
+
+  /* Wait for sequence started */
+
+  while (nrf91_pwm_getreg(priv, NRF91_PWM_EVENTS_SEQSTARTED0_OFFSET) != 1);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: nrf91_pwm_stop
+ *
+ * Description:
+ *   Stop the PWM
+ *
+ ****************************************************************************/
+
+static int nrf91_pwm_stop(struct pwm_lowerhalf_s *dev)
+{
+  struct nrf91_pwm_s *priv = (struct nrf91_pwm_s *)dev;
+
+  DEBUGASSERT(dev);
+
+  /* Stop PWM */
+
+  nrf91_pwm_putreg(priv, NRF91_PWM_TASKS_STOP_OFFSET, 1);
+
+  /* Wait for PWM stopped */
+
+  while (nrf91_pwm_getreg(priv, NRF91_PWM_EVENTS_STOPPED_OFFSET) != 1);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: nrf91_pwm_ioctl
+ *
+ * Description:
+ *   Lower-half logic may support platform-specific ioctl commands
+ *
+ ****************************************************************************/
+
+static int nrf91_pwm_ioctl(struct pwm_lowerhalf_s *dev,
+                           int cmd, unsigned long arg)
+{
+  struct nrf91_pwm_s *priv = (struct nrf91_pwm_s *)dev;
+
+  DEBUGASSERT(dev);
+
+  /* There are no platform-specific ioctl commands */
+
+  UNUSED(priv);
+
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Public Function
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf91_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 NRF91 lower half PWM driver is returned.
+ *   NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+struct pwm_lowerhalf_s *nrf91_pwminitialize(int pwm)
+{
+  struct nrf91_pwm_s *lower = NULL;
+
+  pwminfo("Initialize PWM%u\n", pwm);
+
+  switch (pwm)
+    {
+#ifdef CONFIG_NRF91_PWM0
+      case 0:
+        {
+          lower = &g_nrf91_pwm0;
+          break;
+        }
+#endif
+
+#ifdef CONFIG_NRF91_PWM1
+      case 1:
+        {
+          lower = &g_nrf91_pwm1;
+          break;
+        }
+#endif
+
+#ifdef CONFIG_NRF91_PWM2
+      case 2:
+        {
+          lower = &g_nrf91_pwm2;
+          break;
+        }
+#endif
+
+#ifdef CONFIG_NRF91_PWM3
+      case 3:
+        {
+          lower = &g_nrf91_pwm3;
+          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/nrf91/nrf91_pwm.h b/arch/arm/src/nrf91/nrf91_pwm.h
new file mode 100644
index 00000000000..19e72781876
--- /dev/null
+++ b/arch/arm/src/nrf91/nrf91_pwm.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+ * arch/arm/src/nrf91/nrf91_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_NRF91_NRF91_PWM_H
+#define __ARCH_ARM_SRC_NRF91_NRF91_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_NRF91_PWM_MULTICHAN
+
+#  ifdef CONFIG_NRF91_PWM0
+#    if !defined(CONFIG_NRF91_PWM0_CHANNEL)
+#      error "CONFIG_NRF91_PWM0_CHANNEL must be provided"
+#    elif CONFIG_NRF91_PWM0_CHANNEL == 0
+#      define CONFIG_NRF91_PWM0_CH0 1
+#    elif CONFIG_NRF91_PWM0_CHANNEL == 1
+#      define CONFIG_NRF91_PWM0_CH1 1
+#    elif CONFIG_NRF91_PWM0_CHANNEL == 2
+#      define CONFIG_NRF91_PWM0_CH2 1
+#    elif CONFIG_NRF91_PWM0_CHANNEL == 3
+#      define CONFIG_NRF91_PWM0_CH3 1
+#    else
+#      error "Unsupported value of CONFIG_NRF91_PWM0_CHANNEL"
+#    endif
+#  endif
+
+#  ifdef CONFIG_NRF91_PWM1
+#    if !defined(CONFIG_NRF91_PWM1_CHANNEL)
+#      error "CONFIG_NRF91_PWM1_CHANNEL must be provided"
+#    elif CONFIG_NRF91_PWM1_CHANNEL == 0
+#      define CONFIG_NRF91_PWM1_CH0 1
+#    elif CONFIG_NRF91_PWM1_CHANNEL == 1
+#      define CONFIG_NRF91_PWM1_CH1 1
+#    elif CONFIG_NRF91_PWM1_CHANNEL == 2
+#      define CONFIG_NRF91_PWM1_CH2 1
+#    elif CONFIG_NRF91_PWM1_CHANNEL == 3
+#      define CONFIG_NRF91_PWM1_CH3 1
+#    else
+#      error "Unsupported value of CONFIG_NRF91_PWM1_CHANNEL"
+#    endif
+#  endif
+
+#  ifdef CONFIG_NRF91_PWM2
+#    if !defined(CONFIG_NRF91_PWM2_CHANNEL)
+#      error "CONFIG_NRF91_PWM2_CHANNEL must be provided"
+#    elif CONFIG_NRF91_PWM2_CHANNEL == 0
+#      define CONFIG_NRF91_PWM2_CH0 1
+#    elif CONFIG_NRF91_PWM2_CHANNEL == 1
+#      define CONFIG_NRF91_PWM2_CH1 1
+#    elif CONFIG_NRF91_PWM2_CHANNEL == 2
+#      define CONFIG_NRF91_PWM2_CH2 1
+#    elif CONFIG_NRF91_PWM2_CHANNEL == 3
+#      define CONFIG_NRF91_PWM2_CH3 1
+#    else
+#      error "Unsupported value of CONFIG_NRF91_PWM2_CHANNEL"
+#    endif
+#  endif
+
+#  ifdef CONFIG_NRF91_PWM3
+#    if !defined(CONFIG_NRF91_PWM3_CHANNEL)
+#      error "CONFIG_NRF91_PWM3_CHANNEL must be provided"
+#    elif CONFIG_NRF91_PWM3_CHANNEL == 0
+#      define CONFIG_NRF91_PWM3_CH0 1
+#    elif CONFIG_NRF91_PWM3_CHANNEL == 1
+#      define CONFIG_NRF91_PWM3_CH1 1
+#    elif CONFIG_NRF91_PWM3_CHANNEL == 2
+#      define CONFIG_NRF91_PWM3_CH2 1
+#    elif CONFIG_NRF91_PWM3_CHANNEL == 3
+#      define CONFIG_NRF91_PWM3_CH3 1
+#    else
+#      error "Unsupported value of CONFIG_NRF91_PWM3_CHANNEL"
+#    endif
+#  endif
+
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nrf91_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 NRF91 lower half PWM driver is returned.
+ *   NULL is returned on any failure.
+ *
+ ****************************************************************************/
+
+struct pwm_lowerhalf_s *nrf91_pwminitialize(int pwm);
+
+#endif /* __ARCH_ARM_SRC_NRF91_NRF91_PWM_H */


Reply via email to