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

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


The following commit(s) were added to refs/heads/master by this push:
     new baf52268cc arch/xmc4: Added pwm driver
baf52268cc is described below

commit baf52268cc05c3e7362a3c64a7a764cefd0c1547
Author: adriendesp <[email protected]>
AuthorDate: Thu Jul 11 12:03:25 2024 +0200

    arch/xmc4: Added pwm driver
---
 arch/arm/src/xmc4/CMakeLists.txt       |    4 +
 arch/arm/src/xmc4/Kconfig              |  185 ++++-
 arch/arm/src/xmc4/Make.defs            |    4 +
 arch/arm/src/xmc4/hardware/xmc4_ccu4.h |   10 +-
 arch/arm/src/xmc4/xmc4_clockconfig.h   |   11 +
 arch/arm/src/xmc4/xmc4_clockutils.c    |   17 +
 arch/arm/src/xmc4/xmc4_pwm.c           | 1365 ++++++++++++++++++++++++++++++++
 arch/arm/src/xmc4/xmc4_pwm.h           |    9 +-
 8 files changed, 1578 insertions(+), 27 deletions(-)

diff --git a/arch/arm/src/xmc4/CMakeLists.txt b/arch/arm/src/xmc4/CMakeLists.txt
index 2903dbd6ee..d46b1a2dbf 100644
--- a/arch/arm/src/xmc4/CMakeLists.txt
+++ b/arch/arm/src/xmc4/CMakeLists.txt
@@ -51,4 +51,8 @@ if(CONFIG_XMC4_USCI_SPI)
   list(APPEND SRCS xmc4_spi.c)
 endif()
 
+if(CONFIG_XMC4_PWM)
+  list(APPEND SRCS xmc4_pwm.c)
+endif()
+
 target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm/src/xmc4/Kconfig b/arch/arm/src/xmc4/Kconfig
index 77437055d9..c6e5ec4cbd 100644
--- a/arch/arm/src/xmc4/Kconfig
+++ b/arch/arm/src/xmc4/Kconfig
@@ -194,6 +194,12 @@ config XMC4_ECAT_P1
        default n
        depends on XMC4_ECAT
 
+config XMC4_PWM
+       bool "Enable Capture Compare Units 4 (CCU4x) for PWM"
+       default n
+       ---help---
+               Support CCU4x
+
 endmenu
 
 menu "XMC4xxx USIC Configuration"
@@ -255,7 +261,7 @@ config XMC4_USIC0_CHAN0_TX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 config XMC4_USIC0_CHAN0_RX_BUFFER_SIZE
@@ -264,7 +270,7 @@ config XMC4_USIC0_CHAN0_RX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 endmenu # USIC0 Channel 0 Configuration
@@ -318,7 +324,7 @@ config XMC4_USIC0_CHAN1_ISI2S
        ---help---
                Configure USIC0 Channel 1 for I2S audio
 
-endchoice # USIC0 Channel 1 Protocol 
+endchoice # USIC0 Channel 1 Protocol
 
 config XMC4_USIC0_CHAN1_TX_BUFFER_SIZE
        int "Tx Fifo Buffer Size"
@@ -326,7 +332,7 @@ config XMC4_USIC0_CHAN1_TX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 config XMC4_USIC0_CHAN1_RX_BUFFER_SIZE
@@ -335,7 +341,7 @@ config XMC4_USIC0_CHAN1_RX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 endmenu # USIC0 Channel 1 Configuration
@@ -388,7 +394,7 @@ config XMC4_USIC1_CHAN0_ISI2S
        ---help---
                Configure USIC1 Channel 0 for I2S audio
 
-endchoice # USIC1 Channel 0 Protocol 
+endchoice # USIC1 Channel 0 Protocol
 
 config XMC4_USIC1_CHAN0_TX_BUFFER_SIZE
        int "Tx Fifo Buffer Size"
@@ -396,7 +402,7 @@ config XMC4_USIC1_CHAN0_TX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 config XMC4_USIC1_CHAN0_RX_BUFFER_SIZE
@@ -405,10 +411,10 @@ config XMC4_USIC1_CHAN0_RX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
-endmenu # USIC1 Channel 0 Configuration 
+endmenu # USIC1 Channel 0 Configuration
 
 menu "USIC1 Channel 1 Configuration"
        depends on XMC4_USIC
@@ -463,19 +469,19 @@ endchoice # USIC1 Channel 1 Protocol
 config XMC4_USIC1_CHAN1_TX_BUFFER_SIZE
        int "Tx Fifo Buffer Size"
        depends on XMC4_USIC1_CHAN1_ISUART
-       default 16 
+       default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 config XMC4_USIC1_CHAN1_RX_BUFFER_SIZE
        int "Rx Fifo Buffer Size"
        depends on XMC4_USIC1_CHAN1_ISUART
-       default 16 
+       default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 endmenu # USIC1 Channel 1 Configuration
@@ -533,19 +539,19 @@ endchoice # USIC2 Channel 0 Protocol
 config XMC4_USIC2_CHAN0_TX_BUFFER_SIZE
        int "Tx Fifo Buffer Size"
        depends on XMC4_USIC2_CHAN0_ISUART
-       default 16 
+       default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 config XMC4_USIC2_CHAN0_RX_BUFFER_SIZE
        int "Rx Fifo Buffer Size"
        depends on XMC4_USIC2_CHAN0_ISUART
-       default 16 
+       default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 endmenu # USIC2 Channel 0 Configuration
@@ -605,7 +611,7 @@ config XMC4_USIC2_CHAN1_TX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 config XMC4_USIC2_CHAN1_RX_BUFFER_SIZE
@@ -614,9 +620,150 @@ config XMC4_USIC2_CHAN1_RX_BUFFER_SIZE
        default 16
        ---help---
                Should be a power of 2 between 2 and 64
-               The sum of Rx and Tx buffers sizes of both 
+               The sum of Rx and Tx buffers sizes of both
                channels should be inferior to 64
 
 endmenu # USIC2 Channel 1 Configuration
 
 endmenu # XMC4xxx USIC Configuration
+
+menu "XMC4xxx PWM Configuration"
+depends on XMC4_PWM
+
+       config XMC4_CCU40
+               bool "Enable CCU40"
+               default n
+               ---help---
+                       Support CCU40
+
+               config XMC4_CCU40_CC40
+                       bool "Enable CCU40 Slice 0 (not compatible with 
tickless)"
+                       default n
+                       depends on XMC4_CCU40 && !CONFIG_SCHED_TICKLESS
+                       ---help---
+                               Support CCU40 CC40, cannot be activated when 
tickless OS is enabled
+
+               config XMC4_CCU40_CC41
+                       bool "Enable CCU40 Slice 1"
+                       default n
+                       depends on XMC4_CCU40
+                       ---help---
+                               Support CCU40 CC41
+
+               config XMC4_CCU40_CC42
+                       bool "Enable CCU40 Slice 2"
+                       default n
+                       depends on XMC4_CCU40
+                       ---help---
+                               Support CCU40 CC42
+
+               config XMC4_CCU40_CC43
+                       bool "Enable CCU40 Slice 3"
+                       default n
+                       depends on XMC4_CCU40
+                       ---help---
+                               Support CCU40 CC43
+
+       config XMC4_CCU41
+               bool "Enable CCU41"
+               default n
+               ---help---
+                       Support CCU41
+
+               config XMC4_CCU41_CC40
+                       bool "Enable CCU41 Slice 0 (not compatible with 
tickless)"
+                       default n
+                       depends on XMC4_CCU41 && !CONFIG_SCHED_TICKLESS
+                       ---help---
+                               Support CCU41 CC40, cannot be activated when 
tickless OS is enabled
+
+               config XMC4_CCU41_CC41
+                       bool "Enable CCU41 Slice 1"
+                       default n
+                       depends on XMC4_CCU41
+                       ---help---
+                               Support CCU41 CC41
+
+               config XMC4_CCU41_CC42
+                       bool "Enable CCU41 Slice 2"
+                       default n
+                       depends on XMC4_CCU41
+                       ---help---
+                               Support CCU41 CC42
+
+               config XMC4_CCU41_CC43
+                       bool "Enable CCU41 Slice 3"
+                       default n
+                       depends on XMC4_CCU41
+                       ---help---
+                               Support CCU41 CC43
+
+       config XMC4_CCU42
+               bool "Enable CCU42"
+               default n
+               ---help---
+                       Support CCU42
+
+               config XMC4_CCU42_CC40
+                       bool "Enable CCU42 Slice 0"
+                       default n
+                       depends on XMC4_CCU42
+                       ---help---
+                               Support CCU42 CC40
+
+               config XMC4_CCU42_CC41
+                       bool "Enable CCU42 Slice 1"
+                       default n
+                       depends on XMC4_CCU42
+                       ---help---
+                               Support CCU42 CC41
+
+               config XMC4_CCU42_CC42
+                       bool "Enable CCU42 Slice 2"
+                       default n
+                       depends on XMC4_CCU42
+                       ---help---
+                               Support CCU42 CC42
+
+               config XMC4_CCU42_CC43
+                       bool "Enable CCU42 Slice 3"
+                       default n
+                       depends on XMC4_CCU42
+                       ---help---
+                               Support CCU42 CC43
+
+       config XMC4_CCU43
+               bool "Enable CCU43"
+               default n
+               ---help---
+                       Support CCU43
+
+               config XMC4_CCU43_CC40
+                       bool "Enable CCU43 Slice 0"
+                       default n
+                       depends on XMC4_CCU43
+                       ---help---
+                               Support CCU43 CC40
+
+               config XMC4_CCU43_CC41
+                       bool "Enable CCU43 Slice 1"
+                       default n
+                       depends on XMC4_CCU43
+                       ---help---
+                               Support CCU43 CC41
+
+               config XMC4_CCU43_CC42
+                       bool "Enable CCU43 Slice 2"
+                       default n
+                       depends on XMC4_CCU43
+                       ---help---
+                               Support CCU43 CC42
+
+               config XMC4_CCU43_CC43
+                       bool "Enable CCU43 Slice 3"
+                       default n
+                       depends on XMC4_CCU43
+                       ---help---
+                               Support CCU43 CC43
+
+endmenu # XMC4xxx PWM Configuration
diff --git a/arch/arm/src/xmc4/Make.defs b/arch/arm/src/xmc4/Make.defs
index f83a771f7c..e7cb6d3b22 100644
--- a/arch/arm/src/xmc4/Make.defs
+++ b/arch/arm/src/xmc4/Make.defs
@@ -55,3 +55,7 @@ endif
 ifeq ($(CONFIG_XMC4_ECAT),y)
 CHIP_CSRCS += xmc4_ecat.c
 endif
+
+ifeq ($(CONFIG_XMC4_PWM),y)
+CHIP_CSRCS += xmc4_pwm.c
+endif
diff --git a/arch/arm/src/xmc4/hardware/xmc4_ccu4.h 
b/arch/arm/src/xmc4/hardware/xmc4_ccu4.h
index 57ae6f0a1e..45bdde2e24 100644
--- a/arch/arm/src/xmc4/hardware/xmc4_ccu4.h
+++ b/arch/arm/src/xmc4/hardware/xmc4_ccu4.h
@@ -783,9 +783,9 @@
 #define CCU4_GSTAT_S2I_SHIFT          (2)                            /* Bits 
2: CC42 IDLE status */
 #define CCU4_GSTAT_S2I_MASK           (1 << CCU4_GSTAT_S2I_SHIFT)
 #define CCU4_GSTAT_S3I_SHIFT          (3)                            /* Bits 
3: CC43 IDLE status */
-#define CCU4_GSTAT_S3I_MASK           (1 << CCU4_GSTAT_SI_SHIFT)
+#define CCU4_GSTAT_S3I_MASK           (1 << CCU4_GSTAT_S3I_SHIFT)
 #define CCU4_GSTAT_PRB_SHIFT          (8)                            /* Bits 
8: Prescaler Run Bit */
-#define CCU4_GSTAT_PRB_MASK           (1 << CCU4_GSTAT_SI_SHIFT)
+#define CCU4_GSTAT_PRB_MASK           (1 << CCU4_GSTAT_PRB_SHIFT)
 
 /* Global Idle Set (GIDLS) */
 
@@ -934,7 +934,7 @@
 /* Input Selector Configuration (CC4yINS) */
 
 #define CCU4_CC4_INS_EV0IS_SHIFT                (0)                            
         /* Bits 0-3: Event 0 signal selection */
-#define CCU4_CC4_INS_EV0IS_MASK                 (15 << 
CCU4_CC4_INS_EV0IS_SHIFT)        
+#define CCU4_CC4_INS_EV0IS_MASK                 (15 << 
CCU4_CC4_INS_EV0IS_SHIFT)
 #   define CCU4_CC4_INS_EV0IS_INA               (0 << 
CCU4_CC4_INS_EV0IS_SHIFT)         /* CCU4x.INyA */
 #   define CCU4_CC4_INS_EV0IS_INB               (1 << 
CCU4_CC4_INS_EV0IS_SHIFT)         /* CCU4x.INyB */
 #   define CCU4_CC4_INS_EV0IS_INC               (2 << 
CCU4_CC4_INS_EV0IS_SHIFT)         /* CCU4x.INyC */
@@ -952,7 +952,7 @@
 #   define CCU4_CC4_INS_EV0IS_INO               (14 << 
CCU4_CC4_INS_EV0IS_SHIFT)        /* CCU4x.INyO */
 #   define CCU4_CC4_INS_EV0IS_INP               (15 << 
CCU4_CC4_INS_EV0IS_SHIFT)        /* CCU4x.INyP */
 #define CCU4_CC4_INS_EV1IS_SHIFT                (4)                            
         /* Bits 4-7: Event 1 signal selection */
-#define CCU4_CC4_INS_EV1IS_MASK                 (15 << 
CCU4_CC4_INS_EV1IS_SHIFT)        
+#define CCU4_CC4_INS_EV1IS_MASK                 (15 << 
CCU4_CC4_INS_EV1IS_SHIFT)
 #   define CCU4_CC4_INS_EV1IS_INA               (0 << 
CCU4_CC4_INS_EV1IS_SHIFT)         /* CCU4x.INyA */
 #   define CCU4_CC4_INS_EV1IS_INB               (1 << 
CCU4_CC4_INS_EV1IS_SHIFT)         /* CCU4x.INyB */
 #   define CCU4_CC4_INS_EV1IS_INC               (2 << 
CCU4_CC4_INS_EV1IS_SHIFT)         /* CCU4x.INyC */
@@ -970,7 +970,7 @@
 #   define CCU4_CC4_INS_EV1IS_INO               (14 << 
CCU4_CC4_INS_EV1IS_SHIFT)        /* CCU4x.INyO */
 #   define CCU4_CC4_INS_EV1IS_INP               (15 << 
CCU4_CC4_INS_EV1IS_SHIFT)        /* CCU4x.INyP */
 #define CCU4_CC4_INS_EV2IS_SHIFT                (8)                            
         /* Bits 8-11: Event 2 signal selection */
-#define CCU4_CC4_INS_EV2IS_MASK                 (15 << 
CCU4_CC4_INS_EV2IS_SHIFT)        
+#define CCU4_CC4_INS_EV2IS_MASK                 (15 << 
CCU4_CC4_INS_EV2IS_SHIFT)
 #   define CCU4_CC4_INS_EV2IS_INA               (0 << 
CCU4_CC4_INS_EV2IS_SHIFT)         /* CCU4x.INyA */
 #   define CCU4_CC4_INS_EV2IS_INB               (1 << 
CCU4_CC4_INS_EV2IS_SHIFT)         /* CCU4x.INyB */
 #   define CCU4_CC4_INS_EV2IS_INC               (2 << 
CCU4_CC4_INS_EV2IS_SHIFT)         /* CCU4x.INyC */
diff --git a/arch/arm/src/xmc4/xmc4_clockconfig.h 
b/arch/arm/src/xmc4/xmc4_clockconfig.h
index 16e8ec7d47..77ccfda046 100644
--- a/arch/arm/src/xmc4/xmc4_clockconfig.h
+++ b/arch/arm/src/xmc4/xmc4_clockconfig.h
@@ -72,4 +72,15 @@ uint32_t xmc4_get_coreclock(void);
 
 uint32_t xmc4_get_periphclock(void);
 
+/****************************************************************************
+ * Name: xmc4_get_ccuclock
+ *
+ * Description:
+ *   The ccu clock is either fCPU or fCPU/2, depending on the state
+ *   of the peripheral divider.
+ *
+ ****************************************************************************/
+
+uint32_t xmc4_get_ccuclock(void);
+
 #endif /* __ARCH_ARM_SRC_XMC4_XMC4_CLOCKCONFIG_H */
diff --git a/arch/arm/src/xmc4/xmc4_clockutils.c 
b/arch/arm/src/xmc4/xmc4_clockutils.c
index 37bd65b99d..3fba1cb3af 100644
--- a/arch/arm/src/xmc4/xmc4_clockutils.c
+++ b/arch/arm/src/xmc4/xmc4_clockutils.c
@@ -186,3 +186,20 @@ uint32_t xmc4_get_periphclock(void)
 
   return periphclock;
 }
+
+/****************************************************************************
+ * Name: xmc4_get_ccuclock
+ *
+ * Description:
+ *   The ccu clock is either fCPU or fCPU/2, depending on the state
+ *   of the peripheral divider.
+ *
+ ****************************************************************************/
+
+uint32_t xmc4_get_ccuclock(void)
+{
+  uint32_t f_cpu = xmc4_get_coreclock();
+  uint32_t f_ccu =
+    f_cpu >> ((uint32_t)(getreg32(XMC4_SCU_CCUCLKCR) & SCU_CCUCLKCR_CCUDIV));
+  return  f_ccu;
+}
diff --git a/arch/arm/src/xmc4/xmc4_pwm.c b/arch/arm/src/xmc4/xmc4_pwm.c
new file mode 100644
index 0000000000..261d7ab047
--- /dev/null
+++ b/arch/arm/src/xmc4/xmc4_pwm.c
@@ -0,0 +1,1365 @@
+/*****************************************************************************
+ * arch/arm/src/xmc4/xmc4_pwm.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "xmc4_pwm.h"
+#include "xmc4_gpio.h"
+#include "xmc4_clockconfig.h"
+#include "hardware/xmc4_ccu4.h"
+#include "hardware/xmc4_scu.h"
+#include "hardware/xmc4_pinmux.h"
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Private Types
+ *****************************************************************************/
+
+/* This structure represents the state of one PWM timer */
+
+struct xmc4_pwm_s
+{
+  const struct pwm_ops_s *ops; /* PWM operations */
+  uint8_t module;              /* CCU4x Module number {0,...,4} */
+  uint8_t slice;               /* CC4y Slice number {0,...,4} */
+  uint8_t prescaler;           /* Clock division for f_tclk */
+  uint32_t frequency;          /* Current frequency setting */
+  ub16_t duty;                 /* Current duty setting */
+  uint32_t base;               /* The base address of the CCU4x module */
+  uint32_t f_tclk;             /* The frequency of the module clock */
+  uint32_t outgpio;            /* The output pin config (set in board.h) */
+};
+
+/*****************************************************************************
+ * Private Function Prototypes
+ *****************************************************************************/
+
+/* PWM Register access */
+
+static inline void xmc4_pwm_putreg32(struct xmc4_pwm_s *priv,
+                                     uint32_t offset,
+                                     uint32_t value);
+static inline uint32_t xmc4_pwm_getreg32(struct xmc4_pwm_s *priv,
+                                         uint32_t offset);
+static void xmc4_pwm_modifyreg32(struct xmc4_pwm_s *priv,
+                                 uint32_t offset,
+                                 uint32_t clearbits,
+                                 uint32_t setbits);
+
+/* PWM driver methods */
+
+static int pwm_setup(struct pwm_lowerhalf_s *dev);
+static int pwm_shutdown(struct pwm_lowerhalf_s *dev);
+static int pwm_start(struct pwm_lowerhalf_s *dev,
+                     const struct pwm_info_s *info);
+static int pwm_stop(struct pwm_lowerhalf_s *dev);
+static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg);
+
+/* PWM helper method */
+
+static int pwm_set_period_match(struct xmc4_pwm_s *priv, uint16_t period_val);
+static int pwm_set_compare_match(struct xmc4_pwm_s *priv,
+                                 uint16_t compare_val);
+static int pwm_set_passive_level(struct xmc4_pwm_s *priv, uint8_t level);
+static int pwm_shadow_transfert(struct xmc4_pwm_s *priv);
+static int pwm_enable_slice_clock(struct xmc4_pwm_s *priv);
+static int pwm_disable_slice_clock(struct xmc4_pwm_s *priv);
+static int pwm_start_slice_timer(struct xmc4_pwm_s *priv);
+static int pwm_stop_slice_timer(struct xmc4_pwm_s *priv);
+static bool pwm_is_slice_timer_running(struct xmc4_pwm_s *priv);
+static int pwm_set_slice_prescaler(struct xmc4_pwm_s *priv,
+                                   uint8_t prescaler);
+static int pwm_start_module_prescaler(struct xmc4_pwm_s *priv);
+static int pwm_stop_module_prescaler(struct xmc4_pwm_s *priv);
+static int pwm_enable_module(struct xmc4_pwm_s *priv);
+static int pwm_disable_module(struct xmc4_pwm_s *priv);
+static bool pwm_is_module_used(struct xmc4_pwm_s *priv);
+static int pwm_compute_config(struct xmc4_pwm_s *priv,
+                          const struct pwm_info_s *info,
+                          uint8_t *prescaler,
+                          uint16_t *period,
+                          uint16_t *compare);
+static int pwm_timer(struct xmc4_pwm_s *priv, const struct pwm_info_s *info);
+
+/*****************************************************************************
+ * Private Data
+ *****************************************************************************/
+
+static const struct pwm_ops_s g_pwmops =
+    {
+        .setup = pwm_setup,
+        .shutdown = pwm_shutdown,
+        .start = pwm_start,
+        .stop = pwm_stop,
+        .ioctl = pwm_ioctl,
+};
+
+#ifdef CONFIG_XMC4_CCU40
+#ifdef CONFIG_XMC4_CCU40_CC40
+static struct xmc4_pwm_s g_pwm00 =
+    {
+        .ops = &g_pwmops,
+        .module = 0,
+        .slice = 0,
+        .base = XMC4_CCU40_BASE,
+        .outgpio = GPIO_CCU40_OUT0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU40_CC41
+static struct xmc4_pwm_s g_pwm01 =
+    {
+        .ops = &g_pwmops,
+        .module = 0,
+        .slice = 1,
+        .base = XMC4_CCU40_BASE,
+        .outgpio = GPIO_CCU40_OUT1,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU40_CC42
+static struct xmc4_pwm_s g_pwm02 =
+    {
+        .ops = &g_pwmops,
+        .module = 0,
+        .slice = 2,
+        .base = XMC4_CCU40_BASE,
+        .outgpio = GPIO_CCU40_OUT2,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU40_CC43
+static struct xmc4_pwm_s g_pwm03 =
+    {
+        .ops = &g_pwmops,
+        .module = 0,
+        .slice = 3,
+        .base = XMC4_CCU40_BASE,
+        .outgpio = GPIO_CCU40_OUT3,
+};
+#endif
+#endif /* CONFIG_XMC4_CCU40 */
+
+#ifdef CONFIG_XMC4_CCU41
+#ifdef CONFIG_XMC4_CCU41_CC40
+static struct xmc4_pwm_s g_pwm10 =
+    {
+        .ops = &g_pwmops,
+        .module = 1,
+        .slice = 0,
+        .base = XMC4_CCU41_BASE,
+        .outgpio = GPIO_CCU41_OUT0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU41_CC41
+static struct xmc4_pwm_s g_pwm11 =
+    {
+        .ops = &g_pwmops,
+        .module = 1,
+        .slice = 1,
+        .base = XMC4_CCU41_BASE,
+        .outgpio = GPIO_CCU41_OUT1,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU41_CC42
+static struct xmc4_pwm_s g_pwm12 =
+    {
+        .ops = &g_pwmops,
+        .module = 1,
+        .slice = 2,
+        .base = XMC4_CCU41_BASE,
+        .outgpio = GPIO_CCU41_OUT2,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU41_CC43
+static struct xmc4_pwm_s g_pwm13 =
+    {
+        .ops = &g_pwmops,
+        .module = 1,
+        .slice = 3,
+        .base = XMC4_CCU41_BASE,
+        .outgpio = GPIO_CCU41_OUT3,
+};
+#endif
+#endif /* CONFIG_XMC4_CCU41 */
+
+#ifdef CONFIG_XMC4_CCU42
+#ifdef CONFIG_XMC4_CCU42_CC40
+static struct xmc4_pwm_s g_pwm20 =
+    {
+        .ops = &g_pwmops,
+        .module = 2,
+        .slice = 0,
+        .base = XMC4_CCU42_BASE,
+        .outgpio = GPIO_CCU42_OUT0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU42_CC41
+static struct xmc4_pwm_s g_pwm21 =
+    {
+        .ops = &g_pwmops,
+        .module = 2,
+        .slice = 1,
+        .base = XMC4_CCU42_BASE,
+        .outgpio = GPIO_CCU42_OUT1,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU42_CC42
+static struct xmc4_pwm_s g_pwm22 =
+    {
+        .ops = &g_pwmops,
+        .module = 2,
+        .slice = 2,
+        .base = XMC4_CCU42_BASE,
+        .outgpio = GPIO_CCU42_OUT2,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU42_CC43
+static struct xmc4_pwm_s g_pwm23 =
+    {
+        .ops = &g_pwmops,
+        .module = 2,
+        .slice = 3,
+        .base = XMC4_CCU42_BASE,
+        .outgpio = GPIO_CCU42_OUT3,
+};
+#endif
+#endif /* CONFIG_XMC4_CCU42 */
+
+#ifdef CONFIG_XMC4_CCU43
+#ifdef CONFIG_XMC4_CCU43_CC40
+static struct xmc4_pwm_s g_pwm30 =
+    {
+        .ops = &g_pwmops,
+        .module = 3,
+        .slice = 0,
+        .base = XMC4_CCU43_BASE,
+        .outgpio = GPIO_CCU43_OUT0,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU43_CC41
+static struct xmc4_pwm_s g_pwm31 =
+    {
+        .ops = &g_pwmops,
+        .module = 3,
+        .slice = 1,
+        .base = XMC4_CCU43_BASE,
+        .outgpio = GPIO_CCU43_OUT1,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU43_CC42
+static struct xmc4_pwm_s g_pwm32 =
+    {
+        .ops = &g_pwmops,
+        .module = 3,
+        .slice = 2,
+        .base = XMC4_CCU43_BASE,
+        .outgpio = GPIO_CCU43_OUT2,
+};
+#endif
+
+#ifdef CONFIG_XMC4_CCU43_CC43
+static struct xmc4_pwm_s g_pwm33 =
+    {
+        .ops = &g_pwmops,
+        .module = 3,
+        .slice = 3,
+        .base = XMC4_CCU43_BASE,
+        .outgpio = GPIO_CCU43_OUT3,
+};
+#endif
+#endif /* CONFIG_XMC4_CCU43 */
+
+/*****************************************************************************
+ * Private Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: xmc4_pwm_putreg32
+ *
+ * Description:
+ *   Put a 32-bit register value by offset
+ *
+ *****************************************************************************/
+
+static inline void xmc4_pwm_putreg32(struct xmc4_pwm_s *priv,
+                                     uint32_t offset,
+                                     uint32_t value)
+{
+  putreg32(value, priv->base + offset);
+}
+
+/*****************************************************************************
+ * Name: xmc4_pwm_getreg32
+ *
+ * Description:
+ *   Get a 32-bit register value by offset
+ *
+ *****************************************************************************/
+
+static inline uint32_t xmc4_pwm_getreg32(struct xmc4_pwm_s *priv,
+                                         uint32_t offset)
+{
+  return getreg32(priv->base + offset);
+}
+
+/*****************************************************************************
+ * Name: xmc4_pwm_modifyreg32
+ *
+ * Description:
+ *   Modify a 32-bit register value by offset
+ *
+ *****************************************************************************/
+
+static void xmc4_pwm_modifyreg32(struct xmc4_pwm_s *priv,
+                                 uint32_t offset,
+                                 uint32_t clearbits,
+                                 uint32_t setbits)
+{
+  modifyreg32(priv->base + offset, clearbits, setbits);
+}
+
+/*****************************************************************************
+ * Name: pwm_set_period_match
+ *
+ * Description:
+ *   Set the period match register (CC4yPRS.PRS).
+ *   Must call pwm_shadow_transfert() after.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_set_period_match(struct xmc4_pwm_s *priv, uint16_t period_val)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t cc4yprs_offset =
+      (uint32_t)(XMC4_CCU4_CC40PRS_OFFSET + 0x0100 * priv->slice);
+
+  xmc4_pwm_putreg32(priv, cc4yprs_offset, period_val);
+
+  pwminfo("PWM CCU4%d,CC4%d period is : %d\n",
+          priv->module, priv->slice, period_val);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_set_compare_match
+ *
+ * Description:
+ *   Set the comapre match register (CC4yCRS.CRS).
+ *   Must call pwm_shadow_transfert() after.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_set_compare_match(struct xmc4_pwm_s *priv,
+                                  uint16_t compare_val)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t cc4ycrs_offset =
+      (uint32_t)(XMC4_CCU4_CC40CRS_OFFSET + 0x0100 * priv->slice);
+
+  xmc4_pwm_putreg32(priv, cc4ycrs_offset, compare_val);
+
+  pwminfo("PWM CCU4%d,CC4%d compare is : %d\n",
+          priv->module, priv->slice, compare_val);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_set_passive_level
+ *
+ * Description:
+ *   Set the passive level of the PWM slice.
+ *   Must call pwm_shadow_transfert() after.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_set_passive_level(struct xmc4_pwm_s *priv, uint8_t level)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t passive_level = (uint32_t)(level == 1);
+
+  uint32_t cc4ypsl_offset =
+      (uint32_t)(XMC4_CCU4_CC40PSL_OFFSET + 0x0100 * priv->slice);
+
+  xmc4_pwm_putreg32(priv, cc4ypsl_offset, passive_level);
+
+  pwminfo("PWM CCU4%d,CC4%d passive level is : %d\n",
+          priv->module, priv->slice, passive_level);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_shadow_transfert
+ *
+ * Description:
+ *   Enable the transfert of the CRS, PRS and PSL registers for the next
+ *   period. Must be called if one of these register is changed.
+ *   Must call pwm_shadow_transfert() after.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_shadow_transfert(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t shadow_transfert_mask = 1 << (priv->slice * 4);
+
+  xmc4_pwm_modifyreg32(priv, XMC4_CCU4_GCSS_OFFSET, 0,
+                       shadow_transfert_mask);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_enable_slice_clock
+ *
+ * Description:
+ *   Enable the prescaller clock for the given slice.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_enable_slice_clock(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t mask = (1 << priv->slice);
+
+  xmc4_pwm_modifyreg32(priv, XMC4_CCU4_GIDLC_OFFSET, 0, mask);
+
+  pwminfo("PWM CCU4%d,CC4%d clock is enabled\n", priv->module, priv->slice);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_disable_slice_clock
+ *
+ * Description:
+ *   Disable the prescaller clock for the given slice.
+ *
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_disable_slice_clock(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t mask = (1 << priv->slice);
+
+  xmc4_pwm_modifyreg32(priv, XMC4_CCU4_GIDLS_OFFSET, 0, mask);
+
+  pwminfo("PWM CCU4%d,CC4%d clock is disabled\n",
+          priv->module, priv->slice);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_start_slice_timer
+ *
+ * Description:
+ *   Start the timer counter for the given slice.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_start_slice_timer(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t cc4ytcset_offset =
+      (uint32_t)(XMC4_CCU4_CC40TCSET_OFFSET + 0x0100 * priv->slice);
+
+  xmc4_pwm_putreg32(priv, cc4ytcset_offset,
+                    (uint32_t)CCU4_CC4_TCSET_TRBS_MASK);
+
+  pwminfo("PWM CCU4%d,CC4%d timer is running\n", priv->module, priv->slice);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_stop_slice_timer
+ *
+ * Description:
+ *   Stop the timer counter for the given slice. Reset the counter.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_stop_slice_timer(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t cc4ytcclr_offset =
+      (uint32_t)(XMC4_CCU4_CC40TCCLR_OFFSET + 0x0100 * priv->slice);
+
+  xmc4_pwm_putreg32(priv, cc4ytcclr_offset,
+                    (uint32_t)(CCU4_CC4_TCCLR_TRBC_MASK |
+                    CCU4_CC4_TCCLR_TCC_MASK));
+
+  pwminfo("PWM CCU4%d,CC4%d timer is stopped\n", priv->module, priv->slice);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_is_slice_timer_running
+ *
+ * Description:
+ *   Get the state (running timer or not) of the slice.
+ *
+ *
+ * Returned Value:
+ *   0 if slice timer is stopped else true if timmer running.
+ *
+ *****************************************************************************/
+
+static bool pwm_is_slice_timer_running(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t cc4ytst_offset =
+      (uint32_t)(XMC4_CCU4_CC40TST_OFFSET + 0x0100 * priv->slice);
+
+  bool status = (bool)((xmc4_pwm_getreg32(priv, cc4ytst_offset) &
+                        (uint32_t)CCU4_CC4_TCST_TRB_MASK) ==
+                        (uint32_t)CCU4_CC4_TCST_TRB_MASK);
+
+  pwminfo("PWM CCU4%d,CC4%d timer status is :%d\n",
+            priv->module, priv->slice, status);
+
+  return status;
+}
+
+/*****************************************************************************
+ * Name: pwm_set_slice_prescaler
+ *
+ * Description:
+ *   Set the value of the prescaler [0-14] in CC4yPSC.PSIV.
+ *   Slice must be restarted for effective change.
+ *   Update the value of prescaler in related xmc4_pwm_s.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_set_slice_prescaler(struct xmc4_pwm_s *priv, uint8_t prescaler)
+{
+  DEBUGASSERT(priv != NULL);
+  DEBUGASSERT(prescaler < 15);
+
+  if (prescaler >= 15)
+    {
+      return -EINVAL;
+    }
+
+  uint32_t cc4ypsc_offset =
+      (uint32_t)(XMC4_CCU4_CC40PSC_OFFSET + 0x0100 * priv->slice);
+
+  xmc4_pwm_putreg32(priv, cc4ypsc_offset, (uint32_t)prescaler);
+
+  priv->prescaler = prescaler;
+
+  pwminfo("PWM CCU4%d,CC4%d prescaler is set to : %d\n",
+            priv->module, priv->slice, prescaler);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_start_module_prescaler
+ *
+ * Description:
+ *   Start the CCU4x module prescaler (CCU4xGIDLC.SPRB)
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_start_module_prescaler(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  xmc4_pwm_modifyreg32(priv, XMC4_CCU4_GIDLC_OFFSET, 0, CCU4_GIDLC_SPRB_MASK);
+
+  pwminfo("PWM CCU4%d prescaler is started\n", priv->module);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_stop_module_prescaler
+ *
+ * Description:
+ *   Stop the CCU4x module prescaler (CCU4xGIDLS.CPRB)
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_stop_module_prescaler(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  xmc4_pwm_modifyreg32(priv, XMC4_CCU4_GIDLS_OFFSET, 0, CCU4_GIDLS_CPRB_MASK);
+
+  pwminfo("PWM CCU4%d prescaler is stopped\n", priv->module);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_enable_module
+ *
+ * Description:
+ *   Enable the CCU4x module (ungate and de-assert reset).
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_enable_module(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  if (priv->module == 3)
+    {
+#ifdef XMC4_SCU_GATING
+      /* Check if peripheral is gated */
+
+      if ((getreg32(XMC4_SCU_CGATSTAT1) && SCU_CGAT1_CCU43))
+        {
+          putreg32(SCU_CGAT1_CCU43, XMC4_SCU_CGATCLR1); /* Ungate it */
+        }
+
+#endif
+      /* Check if peripheral reset is asserted */
+
+      if ((getreg32(XMC4_SCU_PRSTAT1) && SCU_PR1_CCU43RS))
+        {
+          putreg32(SCU_PR1_CCU43RS, XMC4_SCU_PRCLR1); /* De-assert reset */
+        }
+    }
+  else
+    {
+      uint32_t scu_ccu4x_mask = (uint32_t)(1 << (priv->module + 2));
+
+#ifdef XMC4_SCU_GATING
+      if ((getreg32(XMC4_SCU_CGATSTAT0) && scu_ccu4x_mask))
+        {
+          putreg32(scu_ccu4x_mask, XMC4_SCU_CGATCLR0);
+        }
+
+#endif
+      if ((getreg32(XMC4_SCU_PRSTAT0) && scu_ccu4x_mask))
+        {
+          putreg32(scu_ccu4x_mask, XMC4_SCU_PRCLR0);
+        }
+    }
+
+  pwminfo("PWM CCU4%d module is enabled\n", priv->module);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_enable_module
+ *
+ * Description:
+ *   Disable the CCU4x module (gate and assert reset).
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ *****************************************************************************/
+
+static int pwm_disable_module(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  if (priv->module == 3)
+    {
+      /* Assert reset */
+
+      putreg32(SCU_PR1_CCU43RS, XMC4_SCU_PRCLR1);
+
+#ifdef XMC4_SCU_GATING
+      /* Gate clock */
+
+      putreg32(SCU_CGAT1_CCU43, XMC4_SCU_CGATSET1);
+#endif
+    }
+  else
+    {
+      uint32_t scu_ccu4x_mask = (uint32_t)(1 << (priv->module + 2));
+
+      putreg32(scu_ccu4x_mask, XMC4_SCU_PRSET0);
+
+#ifdef XMC4_SCU_GATING
+      putreg32(scu_ccu4x_mask, XMC4_SCU_CGATSET0);
+#endif
+    }
+
+  pwminfo("PWM CCU4%d module is disabled\n", priv->module);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_is_module_used
+ *
+ * Description:
+ *   Get the state (running timer or not) of the slice.
+ *
+ * Returned Value:
+ *   0 if module is idle, else true if one slice or more is running.
+ *
+ *****************************************************************************/
+
+static bool pwm_is_module_used(struct xmc4_pwm_s *priv)
+{
+  DEBUGASSERT(priv != NULL);
+
+  uint32_t idle_status = xmc4_pwm_getreg32(priv, XMC4_CCU4_GSTAT_OFFSET);
+  uint32_t mask = (CCU4_GSTAT_S0I_MASK |
+                   CCU4_GSTAT_S1I_MASK |
+                   CCU4_GSTAT_S2I_MASK |
+                   CCU4_GSTAT_S3I_MASK);
+
+  idle_status &= mask;
+
+  bool is_used = !(idle_status == mask);
+
+  pwminfo("PWM CCU4%d is used ? : %d\n", priv->module, is_used);
+
+  return is_used;
+}
+
+/*****************************************************************************
+ * Name: pwm_compute_config
+ *
+ * Description:
+ *   Compute the prescaler value, compare and period match for the given
+ *   info config.
+ *
+ * Input Parameters:
+ *   priv - 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
+ *
+ *****************************************************************************/
+
+static int pwm_compute_config(struct xmc4_pwm_s *priv,
+                          const struct pwm_info_s *info,
+                          uint8_t *prescaler,
+                          uint16_t *period,
+                          uint16_t *compare)
+{
+  DEBUGASSERT(priv != NULL);
+  DEBUGASSERT(info != NULL);
+  DEBUGASSERT(prescaler != NULL);
+  DEBUGASSERT(period != NULL);
+  DEBUGASSERT(compare != NULL);
+
+  uint32_t f_ccu = xmc4_get_ccuclock();
+
+  pwminfo("PWM f_ccu is %d\n", f_ccu);
+
+  uint32_t f_tclk = 0;
+  uint8_t new_prescaler = 0;
+  uint32_t prs = 0;
+  uint32_t crs = 0;
+
+  uint32_t f_pwm = info->frequency;
+  float duty_cycle = (1.0 - b16tof(info->duty));
+
+  do
+    {
+      f_tclk = f_ccu >> new_prescaler;
+      new_prescaler += 1;
+      prs = f_tclk / f_pwm - 1;
+      crs = (uint32_t)(duty_cycle * (prs + 1));
+    }
+  while ((prs > UINT16_MAX) || (crs > UINT16_MAX));
+
+  if (new_prescaler > 15)
+    {
+      return -EINVAL;
+    }
+
+  *period = (uint16_t)prs;
+  *compare = (uint16_t)crs;
+  *prescaler = (uint8_t)(new_prescaler - 1);
+  priv->f_tclk = f_tclk;
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_timer
+ *
+ * Description:
+ *   (Re-)initialize the timer resources and start the pulsed output
+ *
+ * Input Parameters:
+ *   priv - 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
+ *
+ *****************************************************************************/
+
+static int pwm_timer(struct xmc4_pwm_s *priv, const struct pwm_info_s *info)
+{
+  DEBUGASSERT(priv != NULL);
+  DEBUGASSERT(info != NULL);
+
+  uint8_t new_prescaler;
+  uint16_t crs;
+  uint16_t prs;
+
+  int ret = pwm_compute_config(priv, info, &new_prescaler, &prs, &crs);
+  if (ret < 0)
+    {
+      return -EINVAL;
+    }
+
+  if (info->frequency != priv->frequency)
+    {
+      /* Is slice timer running ? */
+
+      if (pwm_is_slice_timer_running(priv))
+        {
+          /* Slice is running */
+
+          if (new_prescaler == priv->prescaler)
+            {
+              /* Prescaller doesn't change, update shadow transfert */
+
+              pwm_set_period_match(priv, prs);
+              pwm_set_compare_match(priv, crs);
+              pwm_set_passive_level(priv, info->cpol);
+              pwm_shadow_transfert(priv);
+            }
+          else
+            {
+              /* Stop slice to update prescaler */
+
+              pwm_stop_slice_timer(priv);
+              pwm_disable_slice_clock(priv);
+
+              pwm_set_slice_prescaler(priv, new_prescaler);
+
+              pwm_set_compare_match(priv, crs);
+              pwm_set_period_match(priv, prs);
+              pwm_set_passive_level(priv, info->cpol);
+              pwm_shadow_transfert(priv);
+
+              pwm_enable_slice_clock(priv);
+              pwm_start_slice_timer(priv);
+            }
+        }
+      else
+        {
+          /* Slice isn't running, start it */
+
+          pwm_set_slice_prescaler(priv, new_prescaler);
+
+          pwm_set_compare_match(priv, crs);
+          pwm_set_period_match(priv, prs);
+          pwm_set_passive_level(priv, info->cpol);
+          pwm_shadow_transfert(priv);
+
+          pwm_enable_slice_clock(priv);
+          pwm_start_slice_timer(priv);
+        }
+
+      priv->frequency = info->frequency;
+      priv->duty = info->duty;
+    }
+  else
+    {
+      /* Frequency doesn't change, update shadow transfert */
+
+      pwm_set_compare_match(priv, crs);
+      pwm_set_passive_level(priv, info->cpol);
+      pwm_shadow_transfert(priv);
+
+      priv->duty = info->duty;
+    }
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: 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.
+ *
+ * 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_setup(struct pwm_lowerhalf_s *dev)
+{
+  struct xmc4_pwm_s *priv = (struct xmc4_pwm_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  int ret = OK;
+
+  pwminfo("PWM CCU4%d,CC4%d is being used\n", priv->module, priv->slice);
+
+  /* Set the selected GPIO as the CCU4 output alternate function */
+
+  ret = xmc4_gpio_config((gpioconfig_t)priv->outgpio);
+
+  /* Check if CCU4 is clocked in SCU */
+
+  if ((getreg32(XMC4_SCU_CLKSTAT) & (uint32_t)SCU_CLK_CCUC) == 0)
+    {
+      /* Enable CCU4 clock */
+
+      putreg32(SCU_CLK_CCUC, XMC4_SCU_CLKSET);
+    }
+
+  /* Enable CCU clock during sleep */
+
+  if ((getreg32(XMC4_SCU_SLEEPCR) & (uint32_t)(SCU_SLEEPCR_CCUCR)) == 0)
+    {
+      putreg32(SCU_SLEEPCR_CCUCR | SCU_SLEEPCR_SYSSEL, XMC4_SCU_SLEEPCR);
+    }
+
+  /* Activate CCU4x module */
+
+  ret |= pwm_enable_module(priv);
+
+  /* Start the prescaller of the module */
+
+  ret |= pwm_start_module_prescaler(priv);
+
+  return ret;
+}
+
+static int pwm_start(struct pwm_lowerhalf_s *dev,
+                     const struct pwm_info_s *info)
+{
+  struct xmc4_pwm_s *priv = (struct xmc4_pwm_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  pwminfo("PWM CCU4%d,CC4%d is started\n", priv->module, priv->slice);
+
+  return pwm_timer(priv, info);
+}
+
+/*****************************************************************************
+ * Name: pwm_stop
+ *
+ * Description:
+ *   Stop the timer resources.
+ *
+ * Input Parameters:
+ *   priv - A reference to the lower half PWM driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_stop(struct pwm_lowerhalf_s *dev)
+{
+  struct xmc4_pwm_s *priv = (struct xmc4_pwm_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  pwm_stop_slice_timer(priv);
+  pwm_disable_slice_clock(priv);
+
+  priv->frequency = 0;
+  priv->duty = 0;
+
+  pwminfo("PWM CCU4%d,CC4%d is stopped\n", priv->module, priv->slice);
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_stop
+ *
+ * Description:
+ *   Stop the timer resources and disable hardware module.
+ *
+ * Input Parameters:
+ *   priv - A reference to the lower half PWM driver state structure
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure
+ *
+ *****************************************************************************/
+
+static int pwm_shutdown(struct pwm_lowerhalf_s *dev)
+{
+  struct xmc4_pwm_s *priv = (struct xmc4_pwm_s *)dev;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Disable slice */
+
+  pwm_stop(dev);
+
+  /* Disable module if not used anymore */
+
+  if (!pwm_is_module_used(priv))
+    {
+      pwm_disable_module(priv);
+    }
+
+  priv->f_tclk = 0;
+  priv->prescaler = 0;
+
+  return OK;
+}
+
+/*****************************************************************************
+ * Name: pwm_ioctl
+ *
+ * Description:
+ *   Lower-half logic may support platform-specific ioctl commands
+ *
+ *****************************************************************************/
+
+static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, unsigned long arg)
+{
+  struct xmc4_pwm_s *priv = (struct xmc4_pwm_s *)dev;
+
+  DEBUGASSERT(dev);
+
+  /* There are no platform-specific ioctl commands */
+
+  UNUSED(priv);
+
+  return -ENOTTY;
+}
+
+/*****************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: xmc4_pwminitialize
+ *
+ * Description:
+ *   Initialize one timer for use with the upper_level PWM driver.
+ *
+ * Input Parameters:
+ *   module - A number identifying the CCU4x use, in the range of {0,..,3}.
+ *   slice - A number identifying the CC4y use, in the range of {0,..,3}.
+ *
+ * Returned Value:
+ *   On success, a pointer to the XMC4 lower half PWM driver is returned.
+ *   NULL is returned on any failure.
+ *
+ *****************************************************************************/
+
+struct pwm_lowerhalf_s *xmc4_pwminitialize(int module, int slice)
+{
+  struct xmc4_pwm_s *lower = NULL;
+
+  pwminfo("CCU4%u,CC4%u\n", module, slice);
+
+  switch (module)
+  {
+#ifdef CONFIG_XMC4_CCU40
+  case 0:
+  {
+    switch (slice)
+    {
+#ifdef CONFIG_XMC4_CCU40_CC40
+    case 0:
+    {
+      lower = &g_pwm00;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU40_CC41
+    case 1:
+    {
+      lower = &g_pwm01;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU40_CC42
+    case 2:
+    {
+      lower = &g_pwm02;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU40_CC43
+    case 3:
+    {
+      lower = &g_pwm03;
+      break;
+    }
+#endif
+
+    default:
+    {
+      pwmerr("ERROR: No such CCU4%d,CC4%d existing %d\n", module, slice);
+      lower = NULL;
+      goto errout;
+    }
+    }
+
+    break;
+  }
+#endif /* CONFIG_XMC4_CCU40 */
+
+#ifdef CONFIG_XMC4_CCU41
+  case 1:
+  {
+    switch (slice)
+    {
+#ifdef CONFIG_XMC4_CCU41_CC40
+    case 0:
+    {
+      lower = &g_pwm10;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU41_CC41
+    case 1:
+    {
+      lower = &g_pwm11;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU41_CC42
+    case 2:
+    {
+      lower = &g_pwm12;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU41_CC43
+    case 3:
+    {
+      lower = &g_pwm13;
+      break;
+    }
+#endif
+
+    default:
+    {
+      pwmerr("ERROR: No such CCU4%d,CC4%d existing %d\n", module, slice);
+      lower = NULL;
+      goto errout;
+    }
+    }
+
+    break;
+  }
+#endif /* CONFIG_XMC4_CCU41 */
+
+#ifdef CONFIG_XMC4_CCU42
+  case 2:
+  {
+    switch (slice)
+    {
+#ifdef CONFIG_XMC4_CCU42_CC40
+    case 0:
+    {
+      lower = &g_pwm20;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU42_CC41
+    case 1:
+    {
+      lower = &g_pwm21;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU42_CC42
+    case 2:
+    {
+      lower = &g_pwm22;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU42_CC43
+    case 3:
+    {
+      lower = &g_pwm23;
+      break;
+    }
+#endif
+
+    default:
+    {
+      pwmerr("ERROR: No such CCU4%d,CC4%d existing %d\n", module, slice);
+      lower = NULL;
+      goto errout;
+    }
+    }
+
+    break;
+  }
+#endif /* CONFIG_XMC4_CCU42 */
+
+#ifdef CONFIG_XMC4_CCU43
+  case 3:
+  {
+    switch (slice)
+    {
+#ifdef CONFIG_XMC4_CCU43_CC40
+    case 0:
+    {
+      lower = &g_pwm30;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU43_CC41
+    case 1:
+    {
+      lower = &g_pwm31;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU43_CC42
+    case 2:
+    {
+      lower = &g_pwm32;
+      break;
+    }
+#endif
+
+#ifdef CONFIG_XMC4_CCU43_CC43
+    case 3:
+    {
+      lower = &g_pwm33;
+      break;
+    }
+#endif
+
+    default:
+    {
+      pwmerr("ERROR: No such CCU4%d,CC4%d existing %d\n", module, slice);
+      lower = NULL;
+      goto errout;
+    }
+    }
+
+    break;
+  }
+#endif /* CONFIG_XMC4_CCU43 */
+
+  default:
+  {
+    pwmerr("ERROR: No such CCU4%d,CC4%d existing %d\n", module, slice);
+    lower = NULL;
+    goto errout;
+  }
+  }
+
+errout:
+  return (struct pwm_lowerhalf_s *)lower;
+}
diff --git a/arch/arm/src/xmc4/xmc4_pwm.h b/arch/arm/src/xmc4/xmc4_pwm.h
index 644b4ba222..45e92695d1 100644
--- a/arch/arm/src/xmc4/xmc4_pwm.h
+++ b/arch/arm/src/xmc4/xmc4_pwm.h
@@ -29,6 +29,10 @@
 
 #include "chip.h"
 
+#include <nuttx/timers/pwm.h>
+
+#include <arch/board/board.h>
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
@@ -73,7 +77,7 @@ extern "C"
  *
  ****************************************************************************/
 
-struct pwm_lowerhalf_s *xmc4_pwm_initialize(int timer);
+struct pwm_lowerhalf_s *xmc4_pwminitialize(int module, int slice);
 
 #undef EXTERN
 #if defined(__cplusplus)
@@ -81,5 +85,4 @@ struct pwm_lowerhalf_s *xmc4_pwm_initialize(int timer);
 #endif
 
 #endif /* __ASSEMBLY__ */
-#endif /* CONFIG_XMC4_FTMx_PWM */
-#endif /* __ARCH_ARM_SRC_XMC4_XMC4_PWM_H */
+#endif /* __ARCH_ARM_SRC_XMC4_XMC4_PWM_H */
\ No newline at end of file

Reply via email to