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

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

commit 93602a3a724f188ba45cf20806e76129107fee5c
Author: raiden00pl <[email protected]>
AuthorDate: Wed May 20 10:14:30 2026 +0200

    !arch/at32: separate pulse count from PWM driver
    
    BREAKING CHANGE: separate pulse count from PWM driver
    
    Pulse count handling was removed from PWM driver and moved to a separate 
driver.
    For details about this change, look at previous commit.
    
    Signed-off-by: raiden00pl <[email protected]>
---
 arch/arm/src/at32/Kconfig           |  116 ++-
 arch/arm/src/at32/Make.defs         |    5 +-
 arch/arm/src/at32/at32_pulsecount.c | 1887 +++++++++++++++++++++++++++++++++++
 arch/arm/src/at32/at32_pulsecount.h |   39 +
 arch/arm/src/at32/at32_pwm.c        |  562 +----------
 5 files changed, 2038 insertions(+), 571 deletions(-)

diff --git a/arch/arm/src/at32/Kconfig b/arch/arm/src/at32/Kconfig
index 6d43630fef0..a8e821da370 100644
--- a/arch/arm/src/at32/Kconfig
+++ b/arch/arm/src/at32/Kconfig
@@ -1052,7 +1052,6 @@ config AT32_TIM
 config AT32_PWM
        bool
        default n
-       select ARCH_HAVE_PWM_PULSECOUNT
 
 config AT32_CAP
        bool
@@ -1108,7 +1107,6 @@ config ARCH_BOARD_AT32_CUSTOM_CLOCKCONFIG
        ---help---
                Enables special, board-specific AT32 clock configuration.
 
-
 config AT32_DMACAPABLE
        bool "Workaround non-DMA capable memory"
        depends on ARCH_DMA
@@ -3662,6 +3660,113 @@ config AT32_PWM_TRGO
        ---help---
                Enable TRGO support for PWM driver
 
+config AT32_PULSECOUNT
+       bool
+       default n
+       select ARCH_HAVE_PULSECOUNT
+       select PULSECOUNT
+
+config AT32_TIM1_PULSECOUNT
+       bool "TIM1 pulse count"
+       default n
+       depends on AT32_TIM1
+       select AT32_PULSECOUNT
+       ---help---
+               Reserve timer 1 for pulse count output.
+
+if AT32_TIM1_PULSECOUNT
+
+config AT32_TIM1_PULSECOUNT_TDTS
+       int "TIM1 pulse count clock division"
+       default 0
+       range 0 2
+
+config AT32_TIM1_PULSECOUNT_CHANNEL
+       int "TIM1 pulse count channel"
+       default 1
+       range 1 4
+       ---help---
+               Specifies the timer channel {1,..,4}.
+
+config AT32_TIM1_PULSECOUNT_POL
+       int "TIM1 pulse count output polarity"
+       default 0
+       range 0 1
+
+config AT32_TIM1_PULSECOUNT_IDLE
+       int "TIM1 pulse count idle state"
+       default 0
+       range 0 1
+
+endif # AT32_TIM1_PULSECOUNT
+
+config AT32_TIM8_PULSECOUNT
+       bool "TIM8 pulse count"
+       default n
+       depends on AT32_TIM8
+       select AT32_PULSECOUNT
+       ---help---
+               Reserve timer 8 for pulse count output.
+
+if AT32_TIM8_PULSECOUNT
+
+config AT32_TIM8_PULSECOUNT_TDTS
+       int "TIM8 pulse count clock division"
+       default 0
+       range 0 2
+
+config AT32_TIM8_PULSECOUNT_CHANNEL
+       int "TIM8 pulse count channel"
+       default 1
+       range 1 4
+       ---help---
+               Specifies the timer channel {1,..,4}.
+
+config AT32_TIM8_PULSECOUNT_POL
+       int "TIM8 pulse count output polarity"
+       default 0
+       range 0 1
+
+config AT32_TIM8_PULSECOUNT_IDLE
+       int "TIM8 pulse count idle state"
+       default 0
+       range 0 1
+
+endif # AT32_TIM8_PULSECOUNT
+
+config AT32_TIM20_PULSECOUNT
+       bool "TIM20 pulse count"
+       default n
+       depends on AT32_TIM20
+       select AT32_PULSECOUNT
+       ---help---
+               Reserve timer 20 for pulse count output.
+
+if AT32_TIM20_PULSECOUNT
+
+config AT32_TIM20_PULSECOUNT_TDTS
+       int "TIM20 pulse count clock division"
+       default 0
+       range 0 2
+
+config AT32_TIM20_PULSECOUNT_CHANNEL
+       int "TIM20 pulse count channel"
+       default 1
+       range 1 4
+       ---help---
+               Specifies the timer channel {1,..,4}.
+
+config AT32_TIM20_PULSECOUNT_POL
+       int "TIM20 pulse count output polarity"
+       default 0
+       range 0 1
+
+config AT32_TIM20_PULSECOUNT_IDLE
+       int "TIM20 pulse count idle state"
+       default 0
+       range 0 1
+
+endif # AT32_TIM20_PULSECOUNT
 config AT32_TIM1_ADC
        bool "TIM1 ADC"
        default n
@@ -6008,7 +6113,6 @@ config AT32_ADC5_JEXTSEL
 
 endmenu
 
-
 config AT32_USART
        bool
        default n
@@ -6807,7 +6911,6 @@ config AT32_HCIUART_RXDMAPRIO
        ---help---
                Select HCI UART DMA priority.
 
-
 config AT32_HCIUART_SW_RXFLOW
        bool "Use Software UART RTS flow control"
        default n
@@ -6940,7 +7043,6 @@ config AT32_SPI4_DMA_BUFFER
 
 endmenu # SPI Configuration
 
-
 menu "I2C Configuration"
        depends on AT32_I2C
 
@@ -7027,7 +7129,6 @@ config AT32_SDIO_DMAPRIO
                For AT32 , options are: 0x00000000 low, 0x00001000 medium,
                0x00002000 high, 0x00003000 very high.  Default: medium.
 
-
 config AT32_SDIO_WIDTH_D1_ONLY
        bool "Use D1 only"
        default n
@@ -7036,7 +7137,6 @@ config AT32_SDIO_WIDTH_D1_ONLY
 
 endmenu
 
-
 config AT32_HAVE_RTC_COUNTER
        bool
        default n
@@ -7433,7 +7533,6 @@ config OTG_ID_GPIO_DISABLE
 
 endmenu
 
-
 menu "CAN driver configuration"
        depends on AT32_CAN
 
@@ -7488,7 +7587,6 @@ config AT32_CAN_REGDEBUG
 
 endmenu # "CAN driver configuration"
 
-
 menu "AT32 QEncoder Driver"
        depends on SENSORS_QENCODER
        depends on AT32_TIM1 || AT32_TIM2 || AT32_TIM3 || AT32_TIM4 || 
AT32_TIM5 || AT32_TIM8
diff --git a/arch/arm/src/at32/Make.defs b/arch/arm/src/at32/Make.defs
index 851e6b4f504..67be986fd58 100644
--- a/arch/arm/src/at32/Make.defs
+++ b/arch/arm/src/at32/Make.defs
@@ -146,6 +146,10 @@ ifeq ($(CONFIG_AT32_PWM),y)
 CHIP_CSRCS += at32_pwm.c
 endif
 
+ifeq ($(CONFIG_AT32_PULSECOUNT),y)
+CHIP_CSRCS += at32_pulsecount.c
+endif
+
 ifeq ($(CONFIG_AT32_CAP),y)
 CHIP_CSRCS += at32_capture_lowerhalf.c
 endif
@@ -180,4 +184,3 @@ ifeq ($(CONFIG_AT32_FOC),y)
 CHIP_CSRCS += at32_foc.c
 endif
 
-
diff --git a/arch/arm/src/at32/at32_pulsecount.c 
b/arch/arm/src/at32/at32_pulsecount.c
new file mode 100644
index 00000000000..c9501c53891
--- /dev/null
+++ b/arch/arm/src/at32/at32_pulsecount.c
@@ -0,0 +1,1887 @@
+/****************************************************************************
+ * arch/arm/src/at32/at32_pulsecount.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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+
+#include <nuttx/arch.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "at32_pulsecount.h"
+#include "at32_rcc.h"
+#include "at32_gpio.h"
+#include "at32_tim.h"
+
+/* This module then only compiles if there is at least one enabled timer
+ * intended for use with the pulsecount upper half driver.
+ *
+ * It implements support for both:
+ *   1. AT32 TIMER IP version 1 - F43x
+ */
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Timer Definitions ********************************************************/
+
+/* Pulsecount is supported by advanced timers only. */
+
+#define TIMTYPE_ADVANCED  5
+#define TIMTYPE_TIM1      TIMTYPE_ADVANCED
+#define TIMTYPE_TIM8      TIMTYPE_ADVANCED
+#define TIMTYPE_TIM20     TIMTYPE_ADVANCED
+
+/* Advanced timer clock source, RCC EN offset, enable bit,
+ * RCC RST offset, reset bit to use
+ */
+
+#  define TIMCLK_TIM1      AT32_APB2_TIM1_CLKIN
+#  define TIMRCCEN_TIM1    AT32_CRM_APB2EN
+#  define TIMEN_TIM1       CRM_APB2EN_TMR1EN
+#  define TIMRCCRST_TIM1   AT32_CRM_APB2RST
+#  define TIMRST_TIM1      CRM_APB2RST_TMR1RST
+#  define TIMCLK_TIM8      AT32_APB2_TIM8_CLKIN
+#  define TIMRCCEN_TIM8    AT32_CRM_APB2EN
+#  define TIMEN_TIM8       CRM_APB2EN_TMR8EN
+#  define TIMRCCRST_TIM8   AT32_CRM_APB2RST
+#  define TIMRST_TIM8      CRM_APB2RST_TMR8RST
+#  define TIMCLK_TIM20     AT32_APB2_TIM20_CLKIN
+#  define TIMRCCEN_TIM20   AT32_CRM_APB2EN
+#  define TIMEN_TIM20      CRM_APB2EN_TMR20EN
+#  define TIMRCCRST_TIM20  AT32_CRM_APB2RST
+#  define TIMRST_TIM20     CRM_APB2RST_TMR20RST
+
+/* Default GPIO pins state */
+
+#define PINCFG_DEFAULT   (GPIO_INPUT | GPIO_FLOAT)
+
+#define PULSECOUNT_POL_NEG      1
+#define PULSECOUNT_IDLE_ACTIVE  1
+
+/* Debug ********************************************************************/
+
+#ifdef CONFIG_DEBUG_TIMER_INFO
+#  define pulsecount_dumpgpio(p,m) at32_dumpgpio(p,m)
+#else
+#  define pulsecount_dumpgpio(p,m)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Pulsecount output configuration */
+
+struct at32_out_s
+{
+  uint8_t  in_use:1;
+  uint8_t  pol:1;
+  uint8_t  idle:1;
+  uint8_t  _res:5;
+  uint32_t pincfg;
+};
+
+/* Pulsecount channel configuration */
+
+struct at32_chan_s
+{
+  uint8_t           channel;
+  struct at32_out_s out1;
+};
+
+/* This structure represents the state of one pulsecount timer */
+
+struct at32_tim_s
+{
+  struct at32_chan_s  channel;
+  uint8_t             timid:5;
+  uint8_t             timtype:3;
+  uint8_t             t_dts:3;
+  uint8_t             _res:5;
+  uint8_t             irq;
+  uint8_t             prev;
+  uint8_t             curr;
+  uint32_t            count;
+  uint32_t            frequency;
+  uint32_t            base;
+  uint32_t            pclk;
+  void               *handle;
+};
+
+struct at32_pulsecount_s
+{
+  const struct pulsecount_ops_s *ops;
+  struct at32_tim_s *timer;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register access */
+
+static uint32_t pulsecount_getreg(struct at32_tim_s *priv, int offset);
+static void pulsecount_putreg(struct at32_tim_s *priv, int offset,
+                              uint32_t value);
+static void pulsecount_modifyreg(struct at32_tim_s *priv, uint32_t offset,
+                                 uint32_t clearbits, uint32_t setbits);
+
+#ifdef CONFIG_DEBUG_TIMER_INFO
+static void pulsecount_dumpregs(struct pulsecount_lowerhalf_s *dev,
+                                const char *msg);
+#else
+#  define pulsecount_dumpregs(priv,msg)
+#endif
+
+/* Timer management */
+
+static int pulsecount_ccr_update(struct pulsecount_lowerhalf_s *dev,
+                                 uint8_t index, uint32_t ccr);
+static int pulsecount_duty_update(struct pulsecount_lowerhalf_s *dev,
+                                  uint8_t channel, ub16_t duty);
+static int pulsecount_frequency_update(struct pulsecount_lowerhalf_s *dev,
+                                       uint32_t frequency);
+static int pulsecount_timer_configure(struct at32_tim_s *priv);
+static int pulsecount_channel_configure(struct pulsecount_lowerhalf_s *dev,
+                                        uint8_t channel);
+static int pulsecount_output_configure(struct at32_tim_s *priv,
+                                       struct at32_chan_s *chan);
+static int pulsecount_outputs_enable(struct pulsecount_lowerhalf_s *dev,
+                                     uint16_t outputs, bool state);
+static void pulsecount_moe_enable(struct pulsecount_lowerhalf_s *dev,
+                                  bool enable);
+static int pulsecount_configure(struct pulsecount_lowerhalf_s *dev);
+static int pulsecount_timer(struct pulsecount_lowerhalf_s *dev,
+                            const struct pulsecount_info_s *info);
+static int pulsecount_interrupt(struct pulsecount_lowerhalf_s *dev);
+#  ifdef CONFIG_AT32_TIM1_PULSECOUNT
+static int pulsecount_tim1interrupt(int irq, void *context, void *arg);
+#  endif
+#  ifdef CONFIG_AT32_TIM8_PULSECOUNT
+static int pulsecount_tim8interrupt(int irq, void *context, void *arg);
+#  endif
+#  ifdef CONFIG_AT32_TIM20_PULSECOUNT
+static int pulsecount_tim20interrupt(int irq, void *context, void *arg);
+#  endif
+static uint8_t pulsecount_count(uint32_t count);
+
+/* Pulsecount driver methods */
+
+static int pulsecount_ll_setup(struct pulsecount_lowerhalf_s *dev);
+static int pulsecount_ll_shutdown(struct pulsecount_lowerhalf_s *dev);
+
+static int pulsecount_ll_stop(struct pulsecount_lowerhalf_s *dev);
+static int pulsecount_ll_ioctl(struct pulsecount_lowerhalf_s *dev,
+                               int cmd, unsigned long arg);
+
+static int pulsecount_setup(struct pulsecount_lowerhalf_s *dev);
+static int pulsecount_shutdown(struct pulsecount_lowerhalf_s *dev);
+static int pulsecount_start(struct pulsecount_lowerhalf_s *dev,
+                            const struct pulsecount_info_s *info,
+                            void *handle);
+static int pulsecount_stop(struct pulsecount_lowerhalf_s *dev);
+static int pulsecount_ioctl(struct pulsecount_lowerhalf_s *dev,
+                            int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_AT32_TIM1_PULSECOUNT
+
+static struct at32_tim_s g_pulsecount1dev =
+{
+  .channel =
+  {
+    .channel = CONFIG_AT32_TIM1_PULSECOUNT_CHANNEL,
+#if CONFIG_AT32_TIM1_PULSECOUNT_CHANNEL == 1
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM1_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM1_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM1_CH1OUT,
+    },
+#elif CONFIG_AT32_TIM1_PULSECOUNT_CHANNEL == 2
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM1_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM1_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM1_CH2OUT,
+    },
+#elif CONFIG_AT32_TIM1_PULSECOUNT_CHANNEL == 3
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM1_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM1_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM1_CH3OUT,
+    },
+#elif CONFIG_AT32_TIM1_PULSECOUNT_CHANNEL == 4
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM1_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM1_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM1_CH4OUT,
+    },
+#endif
+  },
+  .timid       = 1,
+  .timtype     = TIMTYPE_TIM1,
+  .t_dts       = CONFIG_AT32_TIM1_PULSECOUNT_TDTS,
+  .irq         = AT32_IRQ_TIM1UP,
+  .base        = AT32_TMR1_BASE,
+  .pclk        = TIMCLK_TIM1,
+};
+
+#endif /* CONFIG_AT32_TIM1_PULSECOUNT */
+
+#ifdef CONFIG_AT32_TIM8_PULSECOUNT
+
+static struct at32_tim_s g_pulsecount8dev =
+{
+  .channel =
+  {
+    .channel = CONFIG_AT32_TIM8_PULSECOUNT_CHANNEL,
+#if CONFIG_AT32_TIM8_PULSECOUNT_CHANNEL == 1
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM8_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM8_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM8_CH1OUT,
+    },
+#elif CONFIG_AT32_TIM8_PULSECOUNT_CHANNEL == 2
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM8_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM8_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM8_CH2OUT,
+    },
+#elif CONFIG_AT32_TIM8_PULSECOUNT_CHANNEL == 3
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM8_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM8_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM8_CH3OUT,
+    },
+#elif CONFIG_AT32_TIM8_PULSECOUNT_CHANNEL == 4
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM8_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM8_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM8_CH4OUT,
+    },
+#endif
+  },
+  .timid       = 8,
+  .timtype     = TIMTYPE_TIM8,
+  .t_dts       = CONFIG_AT32_TIM8_PULSECOUNT_TDTS,
+  .irq         = AT32_IRQ_TIM8UP,
+  .base        = AT32_TMR8_BASE,
+  .pclk        = TIMCLK_TIM8,
+};
+
+#endif /* CONFIG_AT32_TIM8_PULSECOUNT */
+
+#ifdef CONFIG_AT32_TIM20_PULSECOUNT
+
+static struct at32_tim_s g_pulsecount20dev =
+{
+  .channel =
+  {
+    .channel = CONFIG_AT32_TIM20_PULSECOUNT_CHANNEL,
+#if CONFIG_AT32_TIM20_PULSECOUNT_CHANNEL == 1
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM20_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM20_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM20_CH1OUT,
+    },
+#elif CONFIG_AT32_TIM20_PULSECOUNT_CHANNEL == 2
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM20_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM20_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM20_CH2OUT,
+    },
+#elif CONFIG_AT32_TIM20_PULSECOUNT_CHANNEL == 3
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM20_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM20_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM20_CH3OUT,
+    },
+#elif CONFIG_AT32_TIM20_PULSECOUNT_CHANNEL == 4
+    .out1 =
+    {
+      .in_use  = 1,
+      .pol     = CONFIG_AT32_TIM20_PULSECOUNT_POL,
+      .idle    = CONFIG_AT32_TIM20_PULSECOUNT_IDLE,
+      .pincfg  = GPIO_TIM20_CH4OUT,
+    },
+#endif
+  },
+  .timid       = 20,
+  .timtype     = TIMTYPE_TIM20,
+  .t_dts       = CONFIG_AT32_TIM20_PULSECOUNT_TDTS,
+  .irq         = AT32_IRQ_TIM20UP,
+  .base        = AT32_TMR20_BASE,
+  .pclk        = TIMCLK_TIM20,
+};
+
+#endif /* CONFIG_AT32_TIM20_PULSECOUNT */
+
+static const struct pulsecount_ops_s g_pulsecountops =
+{
+  .setup       = pulsecount_setup,
+  .shutdown    = pulsecount_shutdown,
+  .start       = pulsecount_start,
+  .stop        = pulsecount_stop,
+  .ioctl       = pulsecount_ioctl,
+};
+
+#ifdef CONFIG_AT32_TIM1_PULSECOUNT
+static struct at32_pulsecount_s g_pulsecount1lower =
+{
+  .ops = &g_pulsecountops,
+  .timer = &g_pulsecount1dev,
+};
+#endif
+
+#ifdef CONFIG_AT32_TIM8_PULSECOUNT
+static struct at32_pulsecount_s g_pulsecount8lower =
+{
+  .ops = &g_pulsecountops,
+  .timer = &g_pulsecount8dev,
+};
+#endif
+
+#ifdef CONFIG_AT32_TIM20_PULSECOUNT
+static struct at32_pulsecount_s g_pulsecount20lower =
+{
+  .ops = &g_pulsecountops,
+  .timer = &g_pulsecount20dev,
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pulsecount_reg_is_32bit
+ ****************************************************************************/
+
+static bool pulsecount_reg_is_32bit(uint8_t timtype, uint32_t offset)
+{
+  bool ret = false;
+
+  if (timtype == TIMTYPE_ADVANCED)
+    {
+      if (offset == AT32_ATIM_CR2_OFFSET ||
+          offset == AT32_ATIM_CCMR1_OFFSET ||
+          offset == AT32_ATIM_CCMR2_OFFSET ||
+          offset == AT32_ATIM_CCER_OFFSET ||
+          offset == AT32_ATIM_BDTR_OFFSET)
+        {
+          ret = true;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_getreg
+ *
+ * Description:
+ *   Read the value of an pulsecount timer register
+ *
+ * Input Parameters:
+ *   priv   - A reference to the pulsecount block status
+ *   offset - The offset to the register to read
+ *
+ * Returned Value:
+ *   The current contents of the specified register
+ *
+ ****************************************************************************/
+
+static uint32_t pulsecount_getreg(struct at32_tim_s *priv, int offset)
+{
+  uint32_t retval = 0;
+
+  if (pulsecount_reg_is_32bit(priv->timtype, offset) == true)
+    {
+      /* 32-bit register */
+
+      retval = getreg32(priv->base + offset);
+    }
+  else
+    {
+      /* 16-bit register */
+
+      retval = getreg16(priv->base + offset);
+    }
+
+  /* Return 32-bit value */
+
+  return retval;
+}
+
+/****************************************************************************
+ * Name: pulsecount_putreg
+ *
+ * Description:
+ *   Read the value of an pulsecount timer register
+ *
+ * Input Parameters:
+ *   priv   - A reference to the pulsecount block status
+ *   offset - The offset to the register to read
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void pulsecount_putreg(struct at32_tim_s *priv, int offset,
+                              uint32_t value)
+{
+  if (pulsecount_reg_is_32bit(priv->timtype, offset) == true)
+    {
+      /* 32-bit register */
+
+      putreg32(value, priv->base + offset);
+    }
+  else
+    {
+      /* 16-bit register */
+
+      putreg16((uint16_t)value, priv->base + offset);
+    }
+}
+
+/****************************************************************************
+ * Name: pulsecount_modifyreg
+ *
+ * Description:
+ *   Modify timer register (32-bit or 16-bit)
+ *
+ * Input Parameters:
+ *   priv    - A reference to the pulsecount block status
+ *   offset  - The offset to the register to read
+ *   clrbits - The bits to clear
+ *   setbits - The bits to set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void pulsecount_modifyreg(struct at32_tim_s *priv, uint32_t offset,
+                                 uint32_t clearbits, uint32_t setbits)
+{
+  if (pulsecount_reg_is_32bit(priv->timtype, offset) == true)
+    {
+      /* 32-bit register */
+
+      modifyreg32(priv->base + offset, clearbits, setbits);
+    }
+  else
+    {
+      /* 16-bit register */
+
+      modifyreg16(priv->base + offset, (uint16_t)clearbits,
+                  (uint16_t)setbits);
+    }
+}
+
+/****************************************************************************
+ * Name: pulsecount_dumpregs
+ *
+ * Description:
+ *   Dump all timer registers.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half pulsecount driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG_TIMER_INFO
+static void pulsecount_dumpregs(struct pulsecount_lowerhalf_s *dev,
+                                const char *msg)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+
+  _info("%s:\n", msg);
+  _info("  CR1: %04x CR2:  %04x SMCR:  %04x DIER:  %04x\n",
+          pulsecount_getreg(priv, AT32_GTIM_CR1_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CR2_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_SMCR_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_DIER_OFFSET));
+
+  _info("   SR: %04x EGR:  %04x CCMR1: %04x CCMR2: %04x\n",
+          pulsecount_getreg(priv, AT32_GTIM_SR_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_EGR_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CCMR1_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CCMR2_OFFSET));
+
+  _info(" CCER: %04x CNT:  %04x PSC:   %04x ARR:   %04x\n",
+          pulsecount_getreg(priv, AT32_GTIM_CCER_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CNT_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_PSC_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_ARR_OFFSET));
+
+  if (priv->timid == 1 || priv->timid == 8 || priv->timid == 20)
+    {
+      _info("  RCR: %04x BDTR: %04x\n",
+          pulsecount_getreg(priv, AT32_ATIM_RCR_OFFSET),
+          pulsecount_getreg(priv, AT32_ATIM_BDTR_OFFSET));
+    }
+
+  _info(" CCR1: %04x CCR2: %04x CCR3:  %04x CCR4:  %04x\n",
+          pulsecount_getreg(priv, AT32_GTIM_CCR1_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CCR2_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CCR3_OFFSET),
+          pulsecount_getreg(priv, AT32_GTIM_CCR4_OFFSET));
+
+  _info("  DCR: %04x DMAR: %04x\n",
+      pulsecount_getreg(priv, AT32_GTIM_DCR_OFFSET),
+      pulsecount_getreg(priv, AT32_GTIM_DMAR_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: pulsecount_ccr_update
+ ****************************************************************************/
+
+static int pulsecount_ccr_update(struct pulsecount_lowerhalf_s *dev,
+                                 uint8_t index, uint32_t ccr)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t offset = 0;
+
+  /* CCR channel indices are one-based to match timer channel numbers. */
+
+  switch (index)
+    {
+      case 1:
+        {
+          offset = AT32_GTIM_CCR1_OFFSET;
+          break;
+        }
+
+      case 2:
+        {
+          offset = AT32_GTIM_CCR2_OFFSET;
+          break;
+        }
+
+      case 3:
+        {
+          offset = AT32_GTIM_CCR3_OFFSET;
+          break;
+        }
+
+      case 4:
+        {
+          offset = AT32_GTIM_CCR4_OFFSET;
+          break;
+        }
+
+      default:
+        {
+          _err("ERROR: No such CCR: %u\n", index);
+          return -EINVAL;
+        }
+    }
+
+  /* Update CCR register */
+
+  pulsecount_putreg(priv, offset, ccr);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pulsecount_duty_update
+ *
+ * Description:
+ *   Try to change only channel duty
+ *
+ * Input Parameters:
+ *   dev     - A reference to the lower half driver state structure
+ *   channel - Channel to by updated
+ *   duty    - New duty
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+static int pulsecount_duty_update(struct pulsecount_lowerhalf_s *dev,
+                                  uint8_t channel, ub16_t duty)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t reload = 0;
+  uint32_t ccr    = 0;
+
+  /* We don't want compilation warnings if no DEBUGASSERT */
+
+  UNUSED(priv);
+
+  DEBUGASSERT(priv != NULL);
+
+  _info("TIM%u channel: %u duty: %08" PRIx32 "\n",
+          priv->timid, channel, duty);
+
+  /* Get the reload values */
+
+  reload = pulsecount_getreg(priv, AT32_GTIM_ARR_OFFSET);
+
+  /* Duty cycle:
+   *
+   * duty cycle = ccr / reload (fractional value)
+   */
+
+  ccr = b16toi(duty * reload + b16HALF);
+
+  _info("ccr: %" PRIu32 "\n", ccr);
+
+  /* Write corresponding CCR register */
+
+  return pulsecount_ccr_update(dev, channel, ccr);
+}
+
+/****************************************************************************
+ * Name: pulsecount_frequency_update
+ *
+ * Description:
+ *   Update a pulsecount timer frequency
+ *
+ ****************************************************************************/
+
+static int pulsecount_frequency_update(struct pulsecount_lowerhalf_s *dev,
+                                       uint32_t frequency)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t reload    = 0;
+  uint32_t timclk    = 0;
+  uint32_t prescaler = 0;
+
+  /* Calculate optimal values for the timer prescaler and for the timer
+   * reload register. If 'frequency' is the desired frequency, then
+   *
+   *   reload = timclk / frequency
+   *   timclk = pclk / presc
+   *
+   * Or,
+   *
+   *   reload = pclk / presc / frequency
+   *
+   * There are many solutions to this, but the best solution will be the one
+   * that has the largest reload value and the smallest prescaler value.
+   * That is the solution that should give us the most accuracy in the timer
+   * control.  Subject to:
+   *
+   *   0 <= presc  <= 65536
+   *   1 <= reload <= 65535
+   *
+   * So presc = pclk / 65535 / frequency would be optimal.
+   *
+   * Example:
+   *
+   *  pclk      = 42 MHz
+   *  frequency = 100 Hz
+   *
+   *  prescaler = 42,000,000 / 65,535 / 100
+   *            = 6.4 (or 7 -- taking the ceiling always)
+   *  timclk    = 42,000,000 / 7
+   *            = 6,000,000
+   *  reload    = 6,000,000 / 100
+   *            = 60,000
+   */
+
+  prescaler = (priv->pclk / frequency + 65534) / 65535;
+  if (prescaler < 1)
+    {
+      prescaler = 1;
+    }
+  else if (prescaler > 65536)
+    {
+      prescaler = 65536;
+    }
+
+  timclk = priv->pclk / prescaler;
+
+  reload = timclk / frequency;
+  if (reload < 2)
+    {
+      reload = 1;
+    }
+  else if (reload > 65535)
+    {
+      reload = 65535;
+    }
+  else
+    {
+      reload--;
+    }
+
+  _info("TIM%u PCLK: %" PRIu32" frequency: %" PRIu32
+          " TIMCLK: %" PRIu32 " "
+          "prescaler: %" PRIu32 " reload: %" PRIu32 "\n",
+          priv->timid, priv->pclk, frequency, timclk, prescaler, reload);
+
+  /* Set the reload and prescaler values */
+
+  pulsecount_putreg(priv, AT32_GTIM_ARR_OFFSET, reload);
+  pulsecount_putreg(priv, AT32_GTIM_PSC_OFFSET, (uint16_t)(prescaler - 1));
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pulsecount_timer_configure
+ *
+ * Description:
+ *   Initial configuration for pulsecount timer
+ *
+ ****************************************************************************/
+
+static int pulsecount_timer_configure(struct at32_tim_s *priv)
+{
+  uint16_t cr1 = 0;
+
+  /* Set up the advanced timer CR1 register. */
+
+  cr1 = pulsecount_getreg(priv, AT32_GTIM_CR1_OFFSET);
+
+  /* Pulsecount always uses edge-aligned up-counting mode. */
+
+  cr1 &= ~(GTIM_CR1_DIR | GTIM_CR1_CMS_MASK);
+  cr1 |= GTIM_CR1_EDGE;
+  cr1 &= ~GTIM_CR1_CKD_MASK;
+  cr1 |= priv->t_dts << GTIM_CR1_CKD_SHIFT;
+
+  /* Enable ARR preload to preserve the previous pulsecount behavior. */
+
+  cr1 |= GTIM_CR1_ARPE;
+
+  /* Write CR1 */
+
+  pulsecount_putreg(priv, AT32_GTIM_CR1_OFFSET, cr1);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pulsecount_channel_configure
+ *
+ * Description:
+ *   Configure pulsecount output compare for a channel
+ *
+ ****************************************************************************/
+
+static int pulsecount_channel_configure(struct pulsecount_lowerhalf_s *dev,
+                                        uint8_t channel)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t chanmode = 0;
+  uint32_t ocmode   = 0;
+  uint32_t ccmr     = 0;
+  uint32_t offset   = 0;
+  int      ret      = OK;
+
+  /* Configure output compare mode */
+
+  chanmode = GTIM_CCMR_MODE_PWM1;
+
+  /* Get CCMR offset */
+
+  switch (channel)
+    {
+      case 1:
+      case 2:
+        {
+          offset = AT32_GTIM_CCMR1_OFFSET;
+          break;
+        }
+
+      case 3:
+      case 4:
+        {
+          offset = AT32_GTIM_CCMR2_OFFSET;
+          break;
+        }
+
+      default:
+        {
+          _err("ERROR: No such channel: %u\n", channel);
+          ret = -EINVAL;
+          goto errout;
+        }
+    }
+
+  /* Get current registers */
+
+  ccmr = pulsecount_getreg(priv, offset);
+
+  /* output compare configuration.
+   * NOTE: The CCMRx registers are identical if the channels are outputs.
+   */
+
+  switch (channel)
+    {
+      /* Configure channel 1/3 */
+
+      case 1:
+      case 3:
+        {
+          /* Reset current channel 1/3 mode configuration */
+
+          ccmr &= ~(ATIM_CCMR1_CC1S_MASK | ATIM_CCMR1_OC1M_MASK |
+                     ATIM_CCMR1_OC1PE);
+
+          /* Configure CC1/3 as output */
+
+          ocmode |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC1S_SHIFT);
+
+          /* Configure Compare 1/3 mode */
+
+          ocmode |= (chanmode << ATIM_CCMR1_OC1M_SHIFT);
+
+          /* Enable CCR1/3 preload */
+
+          ocmode |= ATIM_CCMR1_OC1PE;
+
+#ifdef HAVE_IP_TIMERS_V2
+          /* Reset current OC bit */
+
+          ccmr &= ~(ATIM_CCMR1_OC1M);
+
+          /* Clear the additional OC1/3M bit */
+#endif
+          break;
+        }
+
+      /* Configure channel 2/4 */
+
+      case 2:
+      case 4:
+        {
+          /* Reset current channel 2/4 mode configuration */
+
+          ccmr &= ~(ATIM_CCMR1_CC2S_MASK | ATIM_CCMR1_OC2M_MASK |
+                     ATIM_CCMR1_OC2PE);
+
+          /* Configure CC2/4 as output */
+
+          ocmode |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC2S_SHIFT);
+
+          /* Configure Compare 2/4 mode */
+
+          ocmode |= (chanmode << ATIM_CCMR1_OC2M_SHIFT);
+
+          /* Enable CCR2/4 preload */
+
+          ocmode |= ATIM_CCMR1_OC2PE;
+
+#ifdef HAVE_IP_TIMERS_V2
+          /* Reset current OC bit */
+
+          ccmr &= ~(ATIM_CCMR1_OC2M);
+
+          /* Clear the additional OC2/4M bit */
+#endif
+          break;
+        }
+    }
+
+  /* Set the selected output compare configuration */
+
+  ccmr |= ocmode;
+
+  /* Write CCMRx registers */
+
+  pulsecount_putreg(priv, offset, ccmr);
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_output_configure
+ *
+ * Description:
+ *   Configure pulsecount output for given channel
+ *
+ ****************************************************************************/
+
+static int pulsecount_output_configure(struct at32_tim_s *priv,
+                                       struct at32_chan_s *chan)
+{
+  uint32_t cr2  = 0;
+  uint32_t ccer = 0;
+  uint8_t  channel = 0;
+
+  /* Get channel */
+
+  channel = chan->channel;
+
+  /* Get current registers state */
+
+  cr2  = pulsecount_getreg(priv, AT32_GTIM_CR2_OFFSET);
+  ccer = pulsecount_getreg(priv, AT32_GTIM_CCER_OFFSET);
+
+  /* | OISx | IDLE | advanced timers | CR2 register
+   * | CCxP | POL  | all pulsecount timers | CCER register
+   */
+
+  /* Configure output polarity (all pulsecount timers) */
+
+  if (chan->out1.pol == PULSECOUNT_POL_NEG)
+    {
+      ccer |= (GTIM_CCER_CC1P << ((channel - 1) * 4));
+    }
+  else
+    {
+      ccer &= ~(GTIM_CCER_CC1P << ((channel - 1) * 4));
+    }
+
+  if (priv->timtype == TIMTYPE_ADVANCED)
+    {
+      /* Configure output IDLE State */
+
+      if (chan->out1.idle == PULSECOUNT_IDLE_ACTIVE)
+        {
+          cr2 |= (ATIM_CR2_OIS1 << ((channel - 1) * 2));
+        }
+      else
+        {
+          cr2 &= ~(ATIM_CR2_OIS1 << ((channel - 1) * 2));
+        }
+
+#ifdef HAVE_IP_TIMERS_V2
+      /* Channels 5 and 6 are outside this split. */
+
+      cr2 &= ~(ATIM_CR2_OIS5 | ATIM_CR2_OIS6);
+
+      /* Channels 5 and 6 are outside this split. */
+
+      ccer &= ~(ATIM_CCER_CC5P | ATIM_CCER_CC6P);
+#endif /* HAVE_IP_TIMERS_V2 */
+    }
+
+  /* Write registers */
+
+  pulsecount_modifyreg(priv, AT32_GTIM_CR2_OFFSET, 0, cr2);
+  pulsecount_modifyreg(priv, AT32_GTIM_CCER_OFFSET, 0, ccer);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pulsecount_outputs_enable
+ *
+ * Description:
+ *   Enable/disable given timer pulsecount outputs.
+ *
+ *   NOTE: This is bulk operation - we can enable/disable many outputs
+ *   at one time
+ *
+ * Input Parameters:
+ *   dev     - A reference to the lower half driver state structure
+ *   outputs - outputs to set (look at enum at32_pulsecount_chan_e)
+ *   state   - Enable/disable operation
+ *
+ ****************************************************************************/
+
+static int pulsecount_outputs_enable(struct pulsecount_lowerhalf_s *dev,
+                                     uint16_t outputs, bool state)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t ccer   = 0;
+  uint32_t regval = 0;
+
+  /* Get current register state */
+
+  ccer = pulsecount_getreg(priv, AT32_GTIM_CCER_OFFSET);
+
+  /* Get outputs configuration */
+
+  regval |= ((outputs & (1 << 0))  ? GTIM_CCER_CC1E  : 0);
+  regval |= ((outputs & (1 << 2))  ? GTIM_CCER_CC2E  : 0);
+  regval |= ((outputs & (1 << 4))  ? GTIM_CCER_CC3E  : 0);
+  regval |= ((outputs & (1 << 6))  ? GTIM_CCER_CC4E  : 0);
+
+#ifdef HAVE_IP_TIMERS_V2
+  regval |= ((outputs & (1 << 8))  ? ATIM_CCER_CC5E  : 0);
+  regval |= ((outputs & (1 << 10))  ? ATIM_CCER_CC6E  : 0);
+#endif
+
+  if (state == true)
+    {
+      /* Enable outputs - set bits */
+
+      ccer |= regval;
+    }
+  else
+    {
+      /* Disable outputs - reset bits */
+
+      ccer &= ~regval;
+    }
+
+  /* Write register */
+
+  pulsecount_putreg(priv, AT32_GTIM_CCER_OFFSET, ccer);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pulsecount_moe_enable
+ ****************************************************************************/
+
+static void pulsecount_moe_enable(struct pulsecount_lowerhalf_s *dev,
+                                  bool enable)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+
+  if (enable)
+    {
+      pulsecount_modifyreg(priv, AT32_ATIM_BDTR_OFFSET, 0, ATIM_BDTR_MOE);
+    }
+  else
+    {
+      pulsecount_modifyreg(priv, AT32_ATIM_BDTR_OFFSET, ATIM_BDTR_MOE, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: pulsecount_outputs_from_channels
+ *
+ * Description:
+ *   Get enabled outputs configuration from the pulsecount timer state
+ *
+ ****************************************************************************/
+
+static uint16_t
+pulsecount_outputs_from_channels(struct at32_tim_s *priv, uint8_t selected)
+{
+  uint16_t outputs = 0;
+  uint8_t  channel;
+
+  channel = priv->channel.channel;
+
+  if (channel != 0 && (selected == 0 || channel == selected) &&
+      priv->channel.out1.in_use == 1)
+    {
+      outputs = (1 << ((channel - 1) * 2));
+    }
+
+  return outputs;
+}
+
+/****************************************************************************
+ * Name: pulsecount_configure
+ *
+ * Description:
+ *   Configure pulsecount timer in PULSECOUNT mode
+ *
+ ****************************************************************************/
+
+static int pulsecount_configure(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint16_t outputs = 0;
+  int     ret      = OK;
+
+  /* NOTE: leave timer counter disabled and all outputs disabled! */
+
+  /* Disable the timer until we get it configured */
+
+  pulsecount_modifyreg(priv, AT32_GTIM_CR1_OFFSET, GTIM_CR1_CEN, 0);
+
+  /* Get configured outputs */
+
+  outputs = pulsecount_outputs_from_channels(priv, 0);
+
+  /* Disable configured outputs before the timer is reconfigured. */
+
+  ret = pulsecount_outputs_enable(dev, outputs, false);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Initial timer configuration */
+
+  ret = pulsecount_timer_configure(priv);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Disable software break (enable outputs) */
+
+  pulsecount_moe_enable(dev, true);
+
+  /* Configure timer channels */
+
+  if (priv->channel.channel != 0)
+    {
+      pulsecount_channel_configure(dev, priv->channel.channel);
+      pulsecount_output_configure(priv, &priv->channel);
+    }
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_timer
+ *
+ * Description:
+ *   (Re-)initialize the timer resources and start the pulsed output
+ *
+ * Input Parameters:
+ *   dev  - A reference to the lower half pulsecount driver state structure
+ *   info - A reference to the characteristics of the pulsed output
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ * This split keeps pulsecount as the existing single-channel mode.
+ *
+ ****************************************************************************/
+
+static int pulsecount_timer(struct pulsecount_lowerhalf_s *dev,
+                            const struct pulsecount_info_s *info)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  ub16_t    duty    = 0;
+  uint8_t   channel = 0;
+  uint16_t  outputs = 0;
+  int       ret     = OK;
+
+  /* If we got here then the timer instance supports pulsecount output. */
+
+  DEBUGASSERT(priv != NULL && info != NULL);
+
+  _info("TIM%u channel: %u high: %" PRIu32 " ns low: %" PRIu32
+          " ns count: %" PRIu32 "\n",
+          priv->timid, priv->channel.channel, info->high_ns,
+          info->low_ns, info->count);
+
+  DEBUGASSERT(pulsecount_frequency(info) > 0);
+
+  /* Channel specific setup */
+
+  duty    = pulsecount_duty(info);
+  channel = priv->channel.channel;
+
+  /* Disable all interrupts and DMA requests, clear all pending status */
+
+  pulsecount_putreg(priv, AT32_GTIM_DIER_OFFSET, 0);
+  pulsecount_putreg(priv, AT32_GTIM_SR_OFFSET, 0);
+
+  /* Set timer frequency */
+
+  ret = pulsecount_frequency_update(dev, pulsecount_frequency(info));
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Update duty cycle */
+
+  ret = pulsecount_duty_update(dev, channel, duty);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* If a non-zero repetition count has been selected, then set the
+   * repetition counter to the count-1 (pulsecount_start() has already
+   * assured us that the count value is within range).
+   */
+
+  if (info->count > 0)
+    {
+      /* Save the remaining count and the number of counts that will have
+       * elapsed on the first interrupt.
+       */
+
+      /* If the first interrupt occurs at the end end of the first
+       * repetition count, then the count will be the same as the RCR
+       * value.
+       */
+
+      priv->prev  = pulsecount_count(info->count);
+      pulsecount_putreg(priv, AT32_ATIM_RCR_OFFSET, priv->prev - 1);
+
+      /* Generate an update event to reload the prescaler.  This should
+       * preload the RCR into active repetition counter.
+       */
+
+      pulsecount_putreg(priv, AT32_GTIM_EGR_OFFSET, GTIM_EGR_UG);
+
+      /* Now set the value of the RCR that will be loaded on the next
+       * update event.
+       */
+
+      priv->count = info->count;
+      priv->curr  = pulsecount_count(info->count - priv->prev);
+      pulsecount_putreg(priv, AT32_ATIM_RCR_OFFSET, priv->curr - 1);
+    }
+
+  /* Otherwise, just clear the repetition counter */
+
+  else
+    {
+      /* Set the repetition counter to zero */
+
+      pulsecount_putreg(priv, AT32_ATIM_RCR_OFFSET, 0);
+
+      /* Generate an update event to reload the prescaler */
+
+      pulsecount_putreg(priv, AT32_GTIM_EGR_OFFSET, GTIM_EGR_UG);
+    }
+
+  /* Get configured outputs */
+
+  outputs = pulsecount_outputs_from_channels(priv, channel);
+
+  /* Enable output */
+
+  ret = pulsecount_outputs_enable(dev, outputs, true);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Setup update interrupt. If info->count is > 0, then we can
+   * be assured that pulsecount_start() has already verified: (1) that
+   * this is an advanced timer, and that (2) the repetition count is within
+   * range.
+   */
+
+  if (info->count > 0)
+    {
+      /* Clear all pending interrupts and enable the update interrupt. */
+
+      pulsecount_putreg(priv, AT32_GTIM_SR_OFFSET, 0);
+      pulsecount_putreg(priv, AT32_GTIM_DIER_OFFSET, GTIM_DIER_UIE);
+
+      /* Enable the timer */
+
+      pulsecount_modifyreg(priv, AT32_GTIM_CR1_OFFSET, 0, GTIM_CR1_CEN);
+
+      /* And enable timer interrupts at the NVIC */
+
+      up_enable_irq(priv->irq);
+    }
+
+  pulsecount_dumpregs(dev, "After starting");
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_interrupt
+ *
+ * Description:
+ *   Handle timer interrupts.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half pulsecount driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+static int pulsecount_interrupt(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint16_t regval;
+
+  /* Verify that this is an update interrupt.  Nothing else is expected. */
+
+  regval = pulsecount_getreg(priv, AT32_ATIM_SR_OFFSET);
+  DEBUGASSERT((regval & ATIM_SR_UIF) != 0);
+
+  /* Clear the UIF interrupt bit */
+
+  pulsecount_putreg(priv, AT32_ATIM_SR_OFFSET, (regval & ~ATIM_SR_UIF));
+
+  /* Calculate the new count by subtracting the number of pulses
+   * since the last interrupt.
+   */
+
+  if (priv->count <= priv->prev)
+    {
+      /* We are finished.  Turn off the master output to stop the output as
+       * quickly as possible.
+       */
+
+      pulsecount_moe_enable(dev, false);
+
+      /* Disable first interrupts, stop and reset the timer */
+
+      pulsecount_ll_stop(dev);
+
+      /* Then perform the callback into the upper half driver */
+
+      pulsecount_expired(priv->handle);
+
+      priv->handle = NULL;
+      priv->count  = 0;
+      priv->prev   = 0;
+      priv->curr   = 0;
+    }
+  else
+    {
+      /* Decrement the count of pulses remaining using the number of
+       * pulses generated since the last interrupt.
+       */
+
+      priv->count -= priv->prev;
+
+      /* Set up the next RCR.  Set 'prev' to the value of the RCR that
+       * was loaded when the update occurred (just before this interrupt)
+       * and set 'curr' to the current value of the RCR register (which
+       * will bet loaded on the next update event).
+       */
+
+      priv->prev = priv->curr;
+      priv->curr = pulsecount_count(priv->count - priv->prev);
+      pulsecount_putreg(priv, AT32_ATIM_RCR_OFFSET, priv->curr - 1);
+    }
+
+  /* Now all of the time critical stuff is done so we can do some debug
+   * output.
+   */
+
+  _info("Update interrupt SR: %04" PRIx16 " prev: %u curr: %u"
+          " count: %" PRIu32 "\n",
+          regval, (unsigned int)priv->prev, (unsigned int)priv->curr,
+          priv->count);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pulsecount_tim1/8interrupt
+ *
+ * Description:
+ *   Handle timer 1 and 8 interrupts.
+ *
+ * Input Parameters:
+ *   Standard NuttX interrupt inputs
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AT32_TIM1_PULSECOUNT
+static int pulsecount_tim1interrupt(int irq, void *context, void *arg)
+{
+  return pulsecount_interrupt((struct pulsecount_lowerhalf_s *)
+                              &g_pulsecount1dev);
+}
+#endif /* CONFIG_AT32_TIM1_PULSECOUNT */
+
+#ifdef CONFIG_AT32_TIM8_PULSECOUNT
+static int pulsecount_tim8interrupt(int irq, void *context, void *arg)
+{
+  return pulsecount_interrupt((struct pulsecount_lowerhalf_s *)
+                              &g_pulsecount8dev);
+}
+#endif /* CONFIG_AT32_TIM8_PULSECOUNT */
+
+#ifdef CONFIG_AT32_TIM20_PULSECOUNT
+static int pulsecount_tim20interrupt(int irq, void *context, void *arg)
+{
+  return pulsecount_interrupt((struct pulsecount_lowerhalf_s *)
+                              &g_pulsecount20dev);
+}
+#endif /* CONFIG_AT32_TIM20_PULSECOUNT */
+
+/****************************************************************************
+ * Name: pulsecount_count
+ *
+ * Description:
+ *   Pick an optimal pulse count to program the RCR.
+ *
+ * Input Parameters:
+ *   count - The total count remaining
+ *
+ * Returned Value:
+ *   The recommended pulse count
+ *
+ ****************************************************************************/
+
+static uint8_t pulsecount_count(uint32_t count)
+{
+  /* Use the advanced-timer repetition counter limit. */
+
+  /* The the remaining pulse count is less than or equal to the maximum, the
+   * just return the count.
+   */
+
+  if (count <= ATIM_RCR_REP_MAX)
+    {
+      return (uint8_t)count;
+    }
+
+  /* Otherwise, we have to be careful.  We do not want a small number of
+   * counts at the end because we might have trouble responding fast enough.
+   * If the remaining count is less than 150% of the maximum, then return
+   * half of the maximum.  In this case the final sequence will be between 64
+   * and 128.
+   */
+
+  else if (count < (3 * ATIM_RCR_REP_MAX / 2))
+    {
+      return (uint8_t)((ATIM_RCR_REP_MAX + 1) >> 1);
+    }
+
+  /* Otherwise, return the maximum.  The final count will be 64 or more */
+
+  else
+    {
+      return (uint8_t)ATIM_RCR_REP_MAX;
+    }
+}
+
+/****************************************************************************
+ * Name: pulsecount_set_apb_clock
+ *
+ * Description:
+ *   Enable or disable APB clock for the timer peripheral
+ *
+ * Input Parameters:
+ *   priv - A reference to the pulsecount block status
+ *   on   - Enable clock if 'on' is 'true' and disable if 'false'
+ *
+ ****************************************************************************/
+
+static int pulsecount_set_apb_clock(struct at32_tim_s *priv, bool on)
+{
+  uint32_t en_bit  = 0;
+  uint32_t regaddr = 0;
+  int      ret     = OK;
+
+  _info("timer %d clock enable: %d\n", priv->timid, on ? 1 : 0);
+
+  /* Determine which timer to configure */
+
+  switch (priv->timid)
+    {
+#ifdef CONFIG_AT32_TIM1_PULSECOUNT
+      case 1:
+        {
+          regaddr = TIMRCCEN_TIM1;
+          en_bit  = TIMEN_TIM1;
+          break;
+        }
+#endif
+
+#ifdef CONFIG_AT32_TIM8_PULSECOUNT
+      case 8:
+        {
+          regaddr = TIMRCCEN_TIM8;
+          en_bit  = TIMEN_TIM8;
+          break;
+        }
+#endif
+
+#ifdef CONFIG_AT32_TIM20_PULSECOUNT
+      case 20:
+        {
+          regaddr = TIMRCCEN_TIM20;
+          en_bit  = TIMEN_TIM20;
+          break;
+        }
+#endif
+
+      default:
+        {
+          _err("ERROR: No such timer configured %d\n", priv->timid);
+          ret = -EINVAL;
+          goto errout;
+        }
+    }
+
+  /* Enable/disable APB 1/2 clock for timer */
+
+  _info("RCC_APBxENR base: %08" PRIx32 "  bits: %04" PRIx32 "\n",
+          regaddr, en_bit);
+
+  if (on)
+    {
+      modifyreg32(regaddr, 0, en_bit);
+    }
+  else
+    {
+      modifyreg32(regaddr, en_bit, 0);
+    }
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_ll_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.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half pulsecount driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ * Assumptions:
+ *   APB1 or 2 clocking for the GPIOs has already been configured by the RCC
+ *   logic at power up.
+ *
+ ****************************************************************************/
+
+static int pulsecount_ll_setup(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t pincfg = 0;
+  int      ret    = OK;
+
+  _info("TIM%u\n", priv->timid);
+
+  /* Enable APB1/2 clocking for timer. */
+
+  ret = pulsecount_set_apb_clock(priv, true);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  pulsecount_dumpregs(dev, "Initially");
+
+  /* Configure the pulsecount output pins, but do not start the timer yet */
+
+  if (priv->channel.out1.in_use == 1)
+    {
+      /* Do not configure the pin if pincfg is not specified.
+       * This prevents overwriting the PA0 configuration if the
+       * channel is used internally.
+       */
+
+      pincfg = priv->channel.out1.pincfg;
+      if (pincfg != 0)
+        {
+          _info("pincfg: %08" PRIx32 "\n", pincfg);
+
+          at32_configgpio(pincfg);
+          pulsecount_dumpgpio(pincfg, "pulsecount setup");
+        }
+    }
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_ll_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
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half pulsecount driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+static int pulsecount_ll_shutdown(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  uint32_t pincfg = 0;
+  int      ret    = OK;
+
+  _info("TIM%u\n", priv->timid);
+
+  /* Make sure that the output has been stopped */
+
+  pulsecount_ll_stop(dev);
+
+  /* Disable APB1/2 clocking for timer. */
+
+  ret = pulsecount_set_apb_clock(priv, false);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+
+  /* Then put the GPIO pins back to the default state */
+
+  pincfg = priv->channel.out1.pincfg;
+  if (pincfg != 0)
+    {
+      _info("pincfg: %08" PRIx32 "\n", pincfg);
+
+      pincfg &= (GPIO_PORT_MASK | GPIO_PIN_MASK);
+      pincfg |= PINCFG_DEFAULT;
+
+      at32_configgpio(pincfg);
+    }
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_ll_stop
+ *
+ * Description:
+ *   Stop the pulsed output and reset the timer resources
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half pulsecount driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ * Assumptions:
+ *   This function is called to stop the pulsed output at anytime.  This
+ *   method is also called from the timer interrupt handler when a repetition
+ *   count expires... automatically stopping the timer.
+ *
+ ****************************************************************************/
+
+static int pulsecount_ll_stop(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+  irqstate_t flags    = 0;
+  uint16_t   outputs  = 0;
+  int        ret = OK;
+
+  _info("TIM%u\n", priv->timid);
+
+  /* Disable interrupts momentary to stop any ongoing timer processing and
+   * to prevent any concurrent access to the reset register.
+   */
+
+  flags = enter_critical_section();
+
+  /* Stopped so frequency is zero */
+
+  priv->frequency = 0;
+
+  /* Disable further interrupts and stop the timer */
+
+  pulsecount_putreg(priv, AT32_GTIM_DIER_OFFSET, 0);
+  pulsecount_putreg(priv, AT32_GTIM_SR_OFFSET, 0);
+
+  /* Disable the timer and timer outputs */
+
+  pulsecount_modifyreg(priv, AT32_GTIM_CR1_OFFSET, GTIM_CR1_CEN, 0);
+  outputs = pulsecount_outputs_from_channels(priv, 0);
+  ret = pulsecount_outputs_enable(dev, outputs, false);
+
+  leave_critical_section(flags);
+
+  pulsecount_dumpregs(dev, "After stop");
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pulsecount_ll_ioctl
+ *
+ * Description:
+ *   Lower-half logic may support platform-specific ioctl commands
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half pulsecount driver state structure
+ *   cmd - The ioctl command
+ *   arg - The argument accompanying the ioctl command
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ ****************************************************************************/
+
+static int pulsecount_ll_ioctl(struct pulsecount_lowerhalf_s *dev, int cmd,
+                               unsigned long arg)
+{
+#ifdef CONFIG_DEBUG_TIMER_INFO
+  struct at32_tim_s *priv = (struct at32_tim_s *)dev;
+
+  /* There are no platform-specific ioctl commands */
+
+  _info("TIM%u\n", priv->timid);
+#endif
+  return -ENOTTY;
+}
+
+static int pulsecount_setup(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_pulsecount_s *pulse = (struct at32_pulsecount_s *)dev;
+  int ret;
+
+  ret = pulsecount_ll_setup((struct pulsecount_lowerhalf_s *)pulse->timer);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  return pulsecount_configure((struct pulsecount_lowerhalf_s *)pulse->timer);
+}
+
+static int pulsecount_shutdown(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_pulsecount_s *pulse = (struct at32_pulsecount_s *)dev;
+  return pulsecount_ll_shutdown((struct pulsecount_lowerhalf_s *)
+                                pulse->timer);
+}
+
+static int pulsecount_start(struct pulsecount_lowerhalf_s *dev,
+                            const struct pulsecount_info_s *info,
+                            void *handle)
+{
+  struct at32_pulsecount_s *pulse = (struct at32_pulsecount_s *)dev;
+  struct at32_tim_s *priv = pulse->timer;
+
+  if (info->count > 0)
+    {
+      if (priv->timtype != TIMTYPE_ADVANCED)
+        {
+          _err("ERROR: TIM%u cannot support pulse count: %" PRIu32 "\n",
+                 priv->timid, info->count);
+          return -EPERM;
+        }
+    }
+
+  priv->handle = handle;
+  return pulsecount_timer((struct pulsecount_lowerhalf_s *)priv, info);
+}
+
+static int pulsecount_stop(struct pulsecount_lowerhalf_s *dev)
+{
+  struct at32_pulsecount_s *pulse = (struct at32_pulsecount_s *)dev;
+  return pulsecount_ll_stop((struct pulsecount_lowerhalf_s *)pulse->timer);
+}
+
+static int pulsecount_ioctl(struct pulsecount_lowerhalf_s *dev,
+                            int cmd, unsigned long arg)
+{
+  struct at32_pulsecount_s *pulse = (struct at32_pulsecount_s *)dev;
+  return pulsecount_ll_ioctl((struct pulsecount_lowerhalf_s *)pulse->timer,
+                             cmd, arg);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+struct pulsecount_lowerhalf_s *at32_pulsecountinitialize(int timer)
+{
+  struct at32_pulsecount_s *lower = NULL;
+
+  _info("TIM%u\n", timer);
+
+  switch (timer)
+    {
+#ifdef CONFIG_AT32_TIM1_PULSECOUNT
+      case 1:
+        {
+          lower = &g_pulsecount1lower;
+          irq_attach(lower->timer->irq, pulsecount_tim1interrupt, NULL);
+          up_disable_irq(lower->timer->irq);
+          break;
+        }
+#endif
+
+#ifdef CONFIG_AT32_TIM8_PULSECOUNT
+      case 8:
+        {
+          lower = &g_pulsecount8lower;
+          irq_attach(lower->timer->irq, pulsecount_tim8interrupt, NULL);
+          up_disable_irq(lower->timer->irq);
+          break;
+        }
+#endif
+
+#ifdef CONFIG_AT32_TIM20_PULSECOUNT
+      case 20:
+        {
+          lower = &g_pulsecount20lower;
+          irq_attach(lower->timer->irq, pulsecount_tim20interrupt, NULL);
+          up_disable_irq(lower->timer->irq);
+          break;
+        }
+#endif
+
+      default:
+        {
+          _err("ERROR: TIM%d does not support pulse count\n", timer);
+          return NULL;
+        }
+    }
+
+  return (struct pulsecount_lowerhalf_s *)lower;
+}
diff --git a/arch/arm/src/at32/at32_pulsecount.h 
b/arch/arm/src/at32/at32_pulsecount.h
new file mode 100644
index 00000000000..9fc6e28d89a
--- /dev/null
+++ b/arch/arm/src/at32/at32_pulsecount.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+ * arch/arm/src/at32/at32_pulsecount.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_AT32_AT32_PULSECOUNT_H
+#define __ARCH_ARM_SRC_AT32_AT32_PULSECOUNT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/timers/pulsecount.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+struct pulsecount_lowerhalf_s *at32_pulsecountinitialize(int timer);
+
+#endif /* __ARCH_ARM_SRC_AT32_AT32_PULSECOUNT_H */
diff --git a/arch/arm/src/at32/at32_pwm.c b/arch/arm/src/at32/at32_pwm.c
index 3867f59c220..1dd25c307d7 100644
--- a/arch/arm/src/at32/at32_pwm.c
+++ b/arch/arm/src/at32/at32_pwm.c
@@ -186,17 +186,6 @@
 #  undef HAVE_ADVTIM
 #endif
 
-/* Pulsecount support */
-
-#ifdef CONFIG_PWM_PULSECOUNT
-#  ifndef HAVE_ADVTIM
-#    error "PWM_PULSECOUNT requires HAVE_ADVTIM"
-#  endif
-#  if defined(CONFIG_AT32_TIM1_PWM) || defined(CONFIG_AT32_TIM8_PWM)
-#    define HAVE_PWM_INTERRUPT
-#  endif
-#endif
-
 /* TRGO/TRGO2 support */
 
 #ifdef CONFIG_AT32_PWM_TRGO
@@ -288,21 +277,12 @@ struct at32_pwmtimer_s
   uint8_t  trgo;                    /* TRGO configuration:
                                      * 4 LSB = TRGO, 4 MSB = TRGO2
                                      */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  uint8_t  irq;                     /* Timer update IRQ */
-  uint8_t  prev;                    /* The previous value of the RCR 
(pre-loaded) */
-  uint8_t  curr;                    /* The current value of the RCR 
(pre-loaded) */
-  uint32_t count;                   /* Remaining pulse count */
 #endif
   uint32_t frequency;               /* Current frequency setting */
   uint32_t base;                    /* The base address of the timer */
   uint32_t pclk;                    /* The frequency of the peripheral
                                      * clock that drives the timer module
                                      */
-#ifdef CONFIG_PWM_PULSECOUNT
-  void *handle;                     /* Handle used for upper-half callback */
-#endif
 };
 
 /****************************************************************************
@@ -363,41 +343,15 @@ static uint16_t pwm_rcr_get(struct pwm_lowerhalf_s *dev);
 static int pwm_rcr_update(struct pwm_lowerhalf_s *dev, uint16_t rcr);
 #endif
 
-#ifdef CONFIG_PWM_PULSECOUNT
-static int pwm_pulsecount_configure(struct pwm_lowerhalf_s *dev);
-#else
 static int pwm_configure(struct pwm_lowerhalf_s *dev);
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-static int pwm_pulsecount_timer(struct pwm_lowerhalf_s *dev,
-                                const struct pwm_info_s *info);
-#endif
 static int pwm_timer(struct pwm_lowerhalf_s *dev,
                      const struct pwm_info_s *info);
-#ifdef HAVE_PWM_INTERRUPT
-static int pwm_interrupt(struct pwm_lowerhalf_s *dev);
-#  ifdef CONFIG_AT32_TIM1_PWM
-static int pwm_tim1interrupt(int irq, void *context, void *arg);
-#  endif
-#  ifdef CONFIG_AT32_TIM8_PWM
-static int pwm_tim8interrupt(int irq, void *context, void *arg);
-#  endif
-#  ifdef CONFIG_AT32_TIM20_PWM
-static int pwm_tim20interrupt(int irq, void *context, void *arg);
-#  endif
-static uint8_t pwm_pulsecount(uint32_t count);
-#endif
 
 /* PWM driver methods */
 
 static int pwm_setup(struct pwm_lowerhalf_s *dev);
 static int pwm_shutdown(struct pwm_lowerhalf_s *dev);
 
-#ifdef CONFIG_PWM_PULSECOUNT
-static int pwm_start_pulsecount(struct pwm_lowerhalf_s *dev,
-                                const struct pwm_info_s *info,
-                                void *handle);
-#endif
 static int pwm_start(struct pwm_lowerhalf_s *dev,
                      const struct pwm_info_s *info);
 
@@ -417,11 +371,7 @@ static const struct pwm_ops_s g_pwmops =
 {
   .setup       = pwm_setup,
   .shutdown    = pwm_shutdown,
-#ifdef CONFIG_PWM_PULSECOUNT
-  .start       = pwm_start_pulsecount,
-#else
   .start       = pwm_start,
-#endif
   .stop        = pwm_stop,
   .ioctl       = pwm_ioctl,
 };
@@ -613,9 +563,6 @@ static struct at32_pwmtimer_s g_pwm1dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM1_TRGO)
   .trgo        = AT32_TIM1_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM1UP,
 #endif
   .base        = AT32_TMR1_BASE,
   .pclk        = TIMCLK_TIM1,
@@ -712,9 +659,6 @@ static struct at32_pwmtimer_s g_pwm2dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM2_TRGO)
   .trgo        = AT32_TIM2_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM2,
 #endif
   .base        = AT32_TMR2_BASE,
   .pclk        = TIMCLK_TIM2,
@@ -811,9 +755,6 @@ static struct at32_pwmtimer_s g_pwm3dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM3_TRGO)
   .trgo        = AT32_TIM3_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM3,
 #endif
   .base        = AT32_TMR3_BASE,
   .pclk        = TIMCLK_TIM3,
@@ -910,9 +851,6 @@ static struct at32_pwmtimer_s g_pwm4dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM4_TRGO)
   .trgo        = AT32_TIM4_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM4,
 #endif
   .base        = AT32_TMR4_BASE,
   .pclk        = TIMCLK_TIM4,
@@ -1007,9 +945,6 @@ static struct at32_pwmtimer_s g_pwm5dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM5_TRGO)
   .trgo        = AT32_TIM5_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM5,
 #endif
   .base        = AT32_TMR5_BASE,
   .pclk        = TIMCLK_TIM5,
@@ -1173,9 +1108,6 @@ static struct at32_pwmtimer_s g_pwm8dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM8_TRGO)
   .trgo        = AT32_TIM8_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM8UP,
 #endif
   .base        = AT32_TMR8_BASE,
   .pclk        = TIMCLK_TIM8,
@@ -1240,9 +1172,6 @@ static struct at32_pwmtimer_s g_pwm9dev =
 #endif
 #if defined(HAVE_TRGO)
   .trgo        = 0,             /* TRGO not supported for TIM9 */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM9,
 #endif
   .base        = AT32_TMR9_BASE,
   .pclk        = TIMCLK_TIM9,
@@ -1291,9 +1220,6 @@ static struct at32_pwmtimer_s g_pwm10dev =
 #endif
 #if defined(HAVE_TRGO)
   .trgo        = 0,             /* TRGO not supported for TIM10 */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM10,
 #endif
   .base        = AT32_TMR10_BASE,
   .pclk        = TIMCLK_TIM10,
@@ -1342,9 +1268,6 @@ static struct at32_pwmtimer_s g_pwm11dev =
 #endif
 #if defined(HAVE_TRGO)
   .trgo        = 0,             /* TRGO not supported for TIM11 */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM11,
 #endif
   .base        = AT32_TMR11_BASE,
   .pclk        = TIMCLK_TIM11,
@@ -1409,9 +1332,6 @@ static struct at32_pwmtimer_s g_pwm12dev =
 #endif
 #if defined(HAVE_TRGO)
   .trgo        = 0,             /* TRGO not supported for TIM12 */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM12,
 #endif
   .base        = AT32_TMR12_BASE,
   .pclk        = TIMCLK_TIM12,
@@ -1460,9 +1380,6 @@ static struct at32_pwmtimer_s g_pwm13dev =
 #endif
 #if defined(HAVE_TRGO)
   .trgo        = 0,             /* TRGO not supported for TIM13 */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM13,
 #endif
   .base        = AT32_TMR13_BASE,
   .pclk        = TIMCLK_TIM13,
@@ -1511,9 +1428,6 @@ static struct at32_pwmtimer_s g_pwm14dev =
 #endif
 #if defined(HAVE_TRGO)
   .trgo        = 0,             /* TRGO not supported for TIM14 */
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM14,
 #endif
   .base        = AT32_TMR14_BASE,
   .pclk        = TIMCLK_TIM14,
@@ -1677,9 +1591,6 @@ static struct at32_pwmtimer_s g_pwm20dev =
 #endif
 #if defined(HAVE_TRGO) && defined(AT32_TIM20_TRGO)
   .trgo        = AT32_TIM20_TRGO,
-#endif
-#ifdef CONFIG_PWM_PULSECOUNT
-  .irq         = AT32_IRQ_TIM20UP,
 #endif
   .base        = AT32_TMR20_BASE,
   .pclk        = TIMCLK_TIM20,
@@ -3088,256 +2999,11 @@ static int pwm_break_dt_configure(struct 
at32_pwmtimer_s *priv)
 }
 #endif
 
-#ifdef CONFIG_PWM_PULSECOUNT
-
-/****************************************************************************
- * Name: pwm_pulsecount_configure
- *
- * Description:
- *   Configure PWM timer in PULSECOUNT mode
- *
- ****************************************************************************/
-
-static int pwm_pulsecount_configure(struct pwm_lowerhalf_s *dev)
-{
-  struct at32_pwmtimer_s *priv = (struct at32_pwmtimer_s *)dev;
-  uint16_t outputs = 0;
-  uint8_t j        = 0;
-  int     ret      = OK;
-
-  /* NOTE: leave timer counter disabled and all outputs disabled! */
-
-  /* Disable the timer until we get it configured */
-
-  pwm_timer_enable(dev, false);
-
-  /* Get configured outputs */
-
-  outputs = pwm_outputs_from_channels(priv);
-
-  /* REVISIT: Disable outputs */
-
-  ret = pwm_outputs_enable(dev, outputs, false);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-  /* Initial timer configuration */
-
-  ret = pwm_timer_configure(priv);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-  /* Configure break and deadtime register */
-
-  ret = pwm_break_dt_configure(priv);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-  /* Disable software break (enable outputs) */
-
-  ret = pwm_soft_break(dev, false);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-#ifdef HAVE_TRGO
-  /* Configure TRGO/TRGO2 */
-
-  ret = pwm_trgo_configure(dev, priv->trgo);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-#endif
-
-  /* Configure timer channels */
-
-  for (j = 0; j < priv->chan_num; j++)
-    {
-      /* Skip channel if not in use */
-
-      if (priv->channels[j].channel != 0)
-        {
-          /* Update PWM mode */
-
-          pwm_mode_configure(dev, priv->channels[j].channel,
-                             priv->channels[j].mode);
-
-          /* PWM outputs configuration */
-
-          pwm_output_configure(priv, &priv->channels[j]);
-        }
-    }
-
-errout:
-  return ret;
-}
-
-/****************************************************************************
- * Name: pwm_pulsecount_timer
- *
- * Description:
- *   (Re-)initialize the timer resources and start the pulsed output
- *
- * Input Parameters:
- *   dev  - A reference to the lower half PWM driver state structure
- *   info - A reference to the characteristics of the pulsed output
- *
- * Returned Value:
- *   Zero on success; a negated errno value on failure
- *
- * TODO: PWM_PULSECOUNT should be configurable for each timer instance
- * TODO: PULSECOUNT doesn't work with MULTICHAN at this moment
- *
- ****************************************************************************/
-
-static int pwm_pulsecount_timer(struct pwm_lowerhalf_s *dev,
-                                const struct pwm_info_s *info)
-{
-  struct at32_pwmtimer_s *priv = (struct at32_pwmtimer_s *)dev;
-  ub16_t    duty    = 0;
-  uint8_t   channel = 0;
-  uint16_t  outputs = 0;
-  int       ret     = OK;
-
-  /* If we got here it means that timer instance support pulsecount mode! */
-
-  DEBUGASSERT(priv != NULL && info != NULL);
-
-  pwminfo("TIM%u channel: %u frequency: %" PRIx32 " duty: %08" PRIx32
-          " count: %" PRIx32 "\n",
-          priv->timid, priv->channels[0].channel, info->frequency,
-          info->channels[0].duty, info->channels[0].count);
-
-  DEBUGASSERT(info->frequency > 0);
-
-  /* Channel specific setup */
-
-  duty    = info->channels[0].duty;
-  channel = priv->channels[0].channel;
-
-  /* Disable all interrupts and DMA requests, clear all pending status */
-
-  pwm_putreg(priv, AT32_GTIM_DIER_OFFSET, 0);
-  pwm_putreg(priv, AT32_GTIM_SR_OFFSET, 0);
-
-  /* Set timer frequency */
-
-  ret = pwm_frequency_update(dev, info->frequency);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-  /* Update duty cycle */
-
-  ret = pwm_duty_update(dev, channel, duty);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-  /* If a non-zero repetition count has been selected, then set the
-   * repetition counter to the count-1 (pwm_pulsecount_start() has already
-   * assured us that the count value is within range).
-   */
-
-  if (info->channels[0].count > 0)
-    {
-      /* Save the remaining count and the number of counts that will have
-       * elapsed on the first interrupt.
-       */
-
-      /* If the first interrupt occurs at the end end of the first
-       * repetition count, then the count will be the same as the RCR
-       * value.
-       */
-
-      priv->prev  = pwm_pulsecount(info->channels[0].count);
-      pwm_rcr_update(dev, priv->prev - 1);
-
-      /* Generate an update event to reload the prescaler.  This should
-       * preload the RCR into active repetition counter.
-       */
-
-      pwm_soft_update(dev);
-
-      /* Now set the value of the RCR that will be loaded on the next
-       * update event.
-       */
-
-      priv->count = info->channels[0].count;
-      priv->curr  = pwm_pulsecount(info->channels[0].count - priv->prev);
-      pwm_rcr_update(dev, priv->curr - 1);
-    }
-
-  /* Otherwise, just clear the repetition counter */
-
-  else
-    {
-      /* Set the repetition counter to zero */
-
-      pwm_rcr_update(dev, 0);
-
-      /* Generate an update event to reload the prescaler */
-
-      pwm_soft_update(dev);
-    }
-
-  /* Get configured outputs */
-
-  outputs = pwm_outputs_from_channels(priv);
-
-  /* Enable output */
-
-  ret = pwm_outputs_enable(dev, outputs, true);
-  if (ret < 0)
-    {
-      goto errout;
-    }
-
-  /* Setup update interrupt. If info->channels[0].count is > 0, then we can
-   * be assured that pwm_pulsecount_start() has already verified: (1) that
-   * this is an advanced timer, and that (2) the repetition count is within
-   * range.
-   */
-
-  if (info->channels[0].count > 0)
-    {
-      /* Clear all pending interrupts and enable the update interrupt. */
-
-      pwm_putreg(priv, AT32_GTIM_SR_OFFSET, 0);
-      pwm_putreg(priv, AT32_GTIM_DIER_OFFSET, GTIM_DIER_UIE);
-
-      /* Enable the timer */
-
-      pwm_timer_enable(dev, true);
-
-      /* And enable timer interrupts at the NVIC */
-
-      up_enable_irq(priv->irq);
-    }
-
-  pwm_dumpregs(dev, "After starting");
-
-errout:
-  return ret;
-}
-
-#endif /* CONFIG_PWM_PULSECOUNT */
-
 /****************************************************************************
  * Name: pwm_configure
  *
  * Description:
- *   Configure PWM timer in normal mode (no PULSECOUNT)
+ *   Configure PWM timer in standard mode
  *
  ****************************************************************************/
 
@@ -3611,173 +3277,6 @@ errout:
   return ret;
 }
 
-#ifdef HAVE_PWM_INTERRUPT
-
-/****************************************************************************
- * Name: pwm_interrupt
- *
- * Description:
- *   Handle timer interrupts.
- *
- * Input Parameters:
- *   dev - A reference to the lower half PWM driver state structure
- *
- * Returned Value:
- *   Zero on success; a negated errno value on failure
- *
- ****************************************************************************/
-
-static int pwm_interrupt(struct pwm_lowerhalf_s *dev)
-{
-  struct at32_pwmtimer_s *priv = (struct at32_pwmtimer_s *)dev;
-  uint16_t regval;
-
-  /* Verify that this is an update interrupt.  Nothing else is expected. */
-
-  regval = pwm_getreg(priv, AT32_ATIM_SR_OFFSET);
-  DEBUGASSERT((regval & ATIM_SR_UIF) != 0);
-
-  /* Clear the UIF interrupt bit */
-
-  pwm_putreg(priv, AT32_ATIM_SR_OFFSET, (regval & ~ATIM_SR_UIF));
-
-  /* Calculate the new count by subtracting the number of pulses
-   * since the last interrupt.
-   */
-
-  if (priv->count <= priv->prev)
-    {
-      /* We are finished.  Turn off the master output to stop the output as
-       * quickly as possible.
-       */
-
-      pwm_soft_break(dev, true);
-
-      /* Disable first interrupts, stop and reset the timer */
-
-      pwm_stop(dev);
-
-      /* Then perform the callback into the upper half driver */
-
-      pwm_expired(priv->handle);
-
-      priv->handle = NULL;
-      priv->count  = 0;
-      priv->prev   = 0;
-      priv->curr   = 0;
-    }
-  else
-    {
-      /* Decrement the count of pulses remaining using the number of
-       * pulses generated since the last interrupt.
-       */
-
-      priv->count -= priv->prev;
-
-      /* Set up the next RCR.  Set 'prev' to the value of the RCR that
-       * was loaded when the update occurred (just before this interrupt)
-       * and set 'curr' to the current value of the RCR register (which
-       * will bet loaded on the next update event).
-       */
-
-      priv->prev = priv->curr;
-      priv->curr = pwm_pulsecount(priv->count - priv->prev);
-      pwm_rcr_update(dev, priv->curr - 1);
-    }
-
-  /* Now all of the time critical stuff is done so we can do some debug
-   * output.
-   */
-
-  pwminfo("Update interrupt SR: %04x prev: %u curr: %u count: %" PRIx32 "\n",
-          regval, priv->prev, priv->curr, priv->count);
-
-  return OK;
-}
-
-/****************************************************************************
- * Name: pwm_tim1/8interrupt
- *
- * Description:
- *   Handle timer 1 and 8 interrupts.
- *
- * Input Parameters:
- *   Standard NuttX interrupt inputs
- *
- * Returned Value:
- *   Zero on success; a negated errno value on failure
- *
- ****************************************************************************/
-
-#ifdef CONFIG_AT32_TIM1_PWM
-static int pwm_tim1interrupt(int irq, void *context, void *arg)
-{
-  return pwm_interrupt((struct pwm_lowerhalf_s *)&g_pwm1dev);
-}
-#endif /* CONFIG_AT32_TIM1_PWM */
-
-#ifdef CONFIG_AT32_TIM8_PWM
-static int pwm_tim8interrupt(int irq, void *context, void *arg)
-{
-  return pwm_interrupt((struct pwm_lowerhalf_s *)&g_pwm8dev);
-}
-#endif /* CONFIG_AT32_TIM8_PWM */
-
-#ifdef CONFIG_AT32_TIM20_PWM
-static int pwm_tim20interrupt(int irq, void *context, void *arg)
-{
-  return pwm_interrupt((struct pwm_lowerhalf_s *)&g_pwm20dev);
-}
-#endif /* CONFIG_AT32_TIM20_PWM */
-
-/****************************************************************************
- * Name: pwm_pulsecount
- *
- * Description:
- *   Pick an optimal pulse count to program the RCR.
- *
- * Input Parameters:
- *   count - The total count remaining
- *
- * Returned Value:
- *   The recommended pulse count
- *
- ****************************************************************************/
-
-static uint8_t pwm_pulsecount(uint32_t count)
-{
-  /* REVISIT: RCR_REP_MAX for GTIM or ATIM ? */
-
-  /* The the remaining pulse count is less than or equal to the maximum, the
-   * just return the count.
-   */
-
-  if (count <= ATIM_RCR_REP_MAX)
-    {
-      return (uint8_t)count;
-    }
-
-  /* Otherwise, we have to be careful.  We do not want a small number of
-   * counts at the end because we might have trouble responding fast enough.
-   * If the remaining count is less than 150% of the maximum, then return
-   * half of the maximum.  In this case the final sequence will be between 64
-   * and 128.
-   */
-
-  else if (count < (3 * ATIM_RCR_REP_MAX / 2))
-    {
-      return (uint8_t)((ATIM_RCR_REP_MAX + 1) >> 1);
-    }
-
-  /* Otherwise, return the maximum.  The final count will be 64 or more */
-
-  else
-    {
-      return (uint8_t)ATIM_RCR_REP_MAX;
-    }
-}
-#endif /* HAVE_PWM_INTERRUPT */
-
 /****************************************************************************
  * Name: pwm_set_apb_clock
  *
@@ -4032,13 +3531,6 @@ static int pwm_setup(struct pwm_lowerhalf_s *dev)
    *       counter, disabled outputs, not configured frequency and duty cycle
    */
 
-#ifdef CONFIG_PWM_PULSECOUNT
-  if (priv->timtype == TIMTYPE_ADVANCED)
-    {
-      ret = pwm_pulsecount_configure(dev);
-    }
-  else
-#endif
     {
       ret = pwm_configure(dev);
     }
@@ -4139,46 +3631,6 @@ errout:
  *
  ****************************************************************************/
 
-#ifdef CONFIG_PWM_PULSECOUNT
-static int pwm_start_pulsecount(struct pwm_lowerhalf_s *dev,
-                                const struct pwm_info_s *info,
-                                void *handle)
-{
-  struct at32_pwmtimer_s *priv = (struct at32_pwmtimer_s *)dev;
-
-  /* Generate an indefinite number of pulses */
-
-  if (info->channels[0].count == 0)
-    {
-      return pwm_start(dev, info);
-    }
-
-  /* Check if a pulsecount has been selected */
-
-  if (info->channels[0].count > 0)
-    {
-      /* Only the advanced timers (TIM1,8 can support the pulse counting)
-       * REVISIT: verify if TIMTYPE_COUNTUP16_N works with it
-       */
-
-      if (priv->timtype != TIMTYPE_ADVANCED)
-        {
-          pwmerr("ERROR: TIM%u cannot support pulse count: %" PRIx32 "\n",
-                 priv->timid, info->channels[0].count);
-          return -EPERM;
-        }
-    }
-
-  /* Save the handle */
-
-  priv->handle = handle;
-
-  /* Start the time */
-
-  return pwm_pulsecount_timer(dev, info);
-}
-#endif /* CONFIG_PWM_PULSECOUNT */
-
 static int pwm_start(struct pwm_lowerhalf_s *dev,
                      const struct pwm_info_s *info)
 {
@@ -4345,10 +3797,6 @@ struct pwm_lowerhalf_s *at32_pwminitialize(int timer)
 
           /* Attach but disable the TIM1 update interrupt */
 
-#ifdef CONFIG_PWM_PULSECOUNT
-          irq_attach(lower->irq, pwm_tim1interrupt, NULL);
-          up_disable_irq(lower->irq);
-#endif
           break;
         }
 #endif
@@ -4392,10 +3840,6 @@ struct pwm_lowerhalf_s *at32_pwminitialize(int timer)
 
           /* Attach but disable the TIM8 update interrupt */
 
-#ifdef CONFIG_PWM_PULSECOUNT
-          irq_attach(lower->irq, pwm_tim8interrupt, NULL);
-          up_disable_irq(lower->irq);
-#endif
           break;
         }
 #endif
@@ -4456,10 +3900,6 @@ struct pwm_lowerhalf_s *at32_pwminitialize(int timer)
 
           /* Attach but disable the TIM20 update interrupt */
 
-#ifdef CONFIG_PWM_PULSECOUNT
-          irq_attach(lower->irq, pwm_tim20interrupt, NULL);
-          up_disable_irq(lower->irq);
-#endif
           break;
         }
 #endif

Reply via email to