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 65e989e063 arch/risc-v: add support for motor control on ESP32|C6|H2
65e989e063 is described below

commit 65e989e0635e13397bb61b2f04b839b197d99de9
Author: Filipe Cavalcanti <[email protected]>
AuthorDate: Wed Jul 3 09:49:11 2024 -0300

    arch/risc-v: add support for motor control on ESP32|C6|H2
---
 .../esp32c6/boards/esp32c6-devkitc/index.rst       |   28 +-
 Documentation/platforms/risc-v/esp32c6/index.rst   |    2 +-
 arch/risc-v/src/common/espressif/Kconfig           |  105 ++
 arch/risc-v/src/common/espressif/esp_mcpwm.c       | 1607 ++++++++++++++++++--
 arch/risc-v/src/common/espressif/esp_mcpwm.h       |   25 +
 .../esp32c6/common/include/esp_board_mcpwm.h       |   22 +-
 boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c |   58 +
 .../esp32c6-devkitc/configs/motor/defconfig        |   52 +
 .../esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c  |    8 +
 .../esp32h2/common/include/esp_board_mcpwm.h       |   22 +-
 boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c |   58 +
 .../esp32h2/esp32h2-devkit/configs/motor/defconfig |   51 +
 .../esp32h2/esp32h2-devkit/src/esp32h2_bringup.c   |    8 +
 13 files changed, 1892 insertions(+), 154 deletions(-)

diff --git 
a/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst 
b/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst
index fa5363ad3c..0536d88071 100644
--- a/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst
+++ b/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst
@@ -105,15 +105,15 @@ capture
 The capture configuration enables the capture driver and the capture example, 
allowing
 the user to measure duty cycle and frequency of a signal. Default pin is GPIO 
18 with
 an internal pull-up resistor enabled. When connecting a 50 Hz pulse with 50% 
duty cycle,
-the following output is expected:
+the following output is expected::
 
-nsh> cap
-cap_main: Hardware initialized. Opening the capture device: /dev/capture0
-cap_main: Number of samples: 0
-pwm duty cycle: 50 % 
-pwm frequence: 50 Hz 
-pwm duty cycle: 50 % 
-pwm frequence: 50 Hz 
+    nsh> cap
+    cap_main: Hardware initialized. Opening the capture device: /dev/capture0
+    cap_main: Number of samples: 0
+    pwm duty cycle: 50 % 
+    pwm frequence: 50 Hz 
+    pwm duty cycle: 50 % 
+    pwm frequence: 50 Hz 
 
 coremark
 --------
@@ -154,6 +154,18 @@ You can scan for all I2C devices using the following 
command::
 
     nsh> i2c dev 0x00 0x7f
 
+motor
+-------
+
+The motor configuration enables the MCPWM peripheral with support to brushed 
DC motor
+control.
+
+It creates a ``/dev/motor0`` device with speed and direction control 
capabilities
+by using two GPIOs (GPIO21 and GPIO22) for PWM output. PWM frequency is 
configurable
+from 25 Hz to 3 kHz, however it defaults to 1 kHz.
+There is also support for an optional fault GPIO (defaults to GPIO9), which 
can be used
+for quick motor braking. All GPIOs are configurable in ``menuconfig``.
+
 mcuboot_nsh
 --------------------
 
diff --git a/Documentation/platforms/risc-v/esp32c6/index.rst 
b/Documentation/platforms/risc-v/esp32c6/index.rst
index 9642de6fcc..72891f568d 100644
--- a/Documentation/platforms/risc-v/esp32c6/index.rst
+++ b/Documentation/platforms/risc-v/esp32c6/index.rst
@@ -170,7 +170,7 @@ I2S              No
 Int. Temp.       No
 LED              No
 LED_PWM          Yes
-MCPWM            Yes (Capture)
+MCPWM            Yes
 Pulse Counter    No
 RMT              No
 RNG              No
diff --git a/arch/risc-v/src/common/espressif/Kconfig 
b/arch/risc-v/src/common/espressif/Kconfig
index 85099a09b2..bc8c68c12c 100644
--- a/arch/risc-v/src/common/espressif/Kconfig
+++ b/arch/risc-v/src/common/espressif/Kconfig
@@ -1309,9 +1309,105 @@ config ESPRESSIF_I2CTIMEOMS
        default 500
 
 endmenu # I2C configuration
+
 menu "MCPWM Configuration"
        depends on ESP_MCPWM
 
+config ESP_MCPWM_MOTOR
+       bool "MCPWM Motor Support"
+       default n
+
+menu "MCPWM Motor Configuration"
+       depends on ESP_MCPWM_MOTOR
+
+config ESP_MCPWM_MOTOR_BDC
+       bool "Brushed DC Motor Control"
+       depends on ESP_MCPWM
+       depends on ESP_MCPWM_MOTOR
+       select MOTOR
+       select MOTOR_UPPER
+       select MOTOR_UPPER_HAVE_SPEED
+       default n
+       ---help---
+               Enables the use of the MCPWM submodule for control of brushed DC
+               motor.
+
+if ESP_MCPWM_MOTOR_BDC
+
+config ESP_MCPWM_MOTOR_CH0
+       bool "Motor Control Channel 0"
+       default n
+       ---help---
+               Enables motor control on channel 0.
+
+if ESP_MCPWM_MOTOR_CH0
+
+config ESP_MCPWM_MOTOR_CH0_PWMA_GPIO
+       int "Output Pin PWM_A"
+       default 20 if ESPRESSIF_ESP32C6
+       default 10 if ESPRESSIF_ESP32H2
+       ---help---
+               Output pin assigned to channel 0 PWM output PWM_A.
+
+config ESP_MCPWM_MOTOR_CH0_PWMB_GPIO
+       int "Output Pin PWM_B"
+       default 21 if ESPRESSIF_ESP32C6
+       default 11 if ESPRESSIF_ESP32H2
+       ---help---
+               Output pin assigned to channel 0 PWM output PWM_B.
+
+config ESP_MCPWM_MOTOR_CH0_PWM_FREQ
+       int "PWM output frequency for channel 0 [Hz]"
+       default 1000
+       ---help---
+               Select PWM frequency for channel 0.
+               Minimum is 25 Hz and maximum is 3000 Hz.
+
+config ESP_MCPMW_MOTOR_CH0_FAULT
+       bool "Enable fault for channel 0"
+       default n
+       ---help---
+               Enables the use of a fault pin to quickly stop the motor when
+               a GPIO pin pulled high.
+
+if ESP_MCPMW_MOTOR_CH0_FAULT
+
+config ESP_MCPMW_MOTOR_CH0_FAULT_GPIO
+       int "GPIO Pin for fault detection"
+       default 9
+       ---help---
+               Input pin assigned to channel 0 fault indicator.
+
+endif # ESP_MCPMW_MOTOR_CH0_FAULT
+
+endif # ESP_MCPWM_MOTOR_CH0
+
+config ESP_MCPWM_MOTOR_CH1
+       bool "Motor Control Channel 1"
+       default n
+       ---help---
+               Enables motor control on channel 1.
+
+if ESP_MCPWM_MOTOR_CH1
+
+config ESP_MCPWM_MOTOR_CH1_PWMA_GPIO
+       int "Output Pin CH1 PWM_A"
+       default 15
+       ---help---
+               Output pin assigned to channel 1 PWM output PWM_A.
+
+config ESP_MCPWM_MOTOR_CH1_PWMB_GPIO
+       int "Output Pin CH1 PWM_B"
+       default 16
+       ---help---
+               Output pin assigned to channel 1 PWM output PWM_B.
+
+endif # ESP_MCPWM_MOTOR_CH1
+
+endif # ESP_MCPWM_MOTOR_BDC
+
+endmenu # MCPWM Motor Settings
+
 config ESP_MCPWM_CAPTURE
        bool "MCPWM Capture Submodule"
        depends on ESP_MCPWM
@@ -1372,6 +1468,15 @@ endif # ESP_MCPWM_CAPTURE_CH2
 
 endif # ESP_MCPWM_CAPTURE
 
+config ESP_MCPWM_TEST_LOOPBACK
+       bool "MCPWM loopback test mode"
+       depends on EXPERIMENTAL
+       default n
+       ---help---
+               This enables a lower-half driver-specific loopback test
+               mode that attaches a capture device to the PWM output on
+               motor tests.
+
 endmenu # MCPWM Configuration
 
 menu "High Resolution Timer"
diff --git a/arch/risc-v/src/common/espressif/esp_mcpwm.c 
b/arch/risc-v/src/common/espressif/esp_mcpwm.c
index e4562e4a95..e172490f45 100644
--- a/arch/risc-v/src/common/espressif/esp_mcpwm.c
+++ b/arch/risc-v/src/common/espressif/esp_mcpwm.c
@@ -33,7 +33,12 @@
 #include <nuttx/arch.h>
 #include <nuttx/irq.h>
 #include <nuttx/spinlock.h>
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
 #include <nuttx/timers/capture.h>
+#endif
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+#include <nuttx/motor/motor.h>
+#endif
 
 #include "esp_gpio.h"
 #include "esp_irq.h"
@@ -58,153 +63,1338 @@
 #  define MCPWM_DEV_CLK_SOURCE SOC_MOD_CLK_PLL_F160M
 #endif
 
-#define MCPWM_DEV_CLK_PRESCALE      4
+#define MCPWM_DEV_CLK_PRESCALE      1
 #define MCPWM_CAPTURE_DEFAULT_GROUP 0
+#define GPIO_IN_FUNCTION            INPUT_FUNCTION_2
+#define GPIO_OUT_FUNCTION           OUTPUT_FUNCTION_2
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC
+
+/* Peak counter at 13330 in up-down mode allows frequencies at a prescale
+ * of: 2 kHz @ 2; 1.5 kHz @ 3; 1.2 kHz @ 4; 1 kHz @ 5.
+ */
+#ifndef CONFIG_ARCH_CHIP_ESP32H2
+#  define PEAK_COUNTER           13330
+#  define MCPWM_MAX_PWM_OUT_FREQ 3000
+#  define MCPWM_MIN_PWM_OUT_FREQ 25
+#else
+#  define PEAK_COUNTER           9595
+#  define MCPWM_MAX_PWM_OUT_FREQ 2500
+#  define MCPWM_MIN_PWM_OUT_FREQ 20
+#endif
+#endif
+#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT
+#  define ESP_MCPMW_MOTOR_FAULT
+#endif
 
 /****************************************************************************
  * Private Types
  ****************************************************************************/
 
-struct mcpwm_dev_common_s
+typedef enum
+{
+  MCPWM_GENERATOR_0,
+  MCPWM_GENERATOR_1,
+  MCPWM_GENERATOR_MAX
+} mcpwm_generator_e;
+
+typedef enum
+{
+  MCPWM_OPERATOR_0,
+  MCPWM_OPERATOR_1,
+  MCPWM_OPERATOR_2,
+  MCPWM_OPERATOR_MAX
+} mcpwm_operator_e;
+
+typedef enum
+{
+  MCPWM_TIMER_0,
+  MCPWM_TIMER_1,
+  MCPWM_TIMER_2,
+  MCPWM_TIMER_MAX
+} mcpwm_timer_e;
+
+typedef enum
+{
+  MCPWM_FAULT_0,
+  MCPWM_FAULT_1,
+  MCPWM_FAULT_2,
+  MCPWM_FAULT_MAX
+} mcpwm_fault_e;
+
+typedef enum
+{
+  MCPWM_MOTOR_CHANNEL_0,
+  MCPWM_MOTOR_CHANNEL_1,
+  MCPWM_MOTOR_CHANNEL_2,
+  MCPWM_MOTOR_CHANNEL_MAX
+} mcpwm_motor_channel_e;
+
+enum mcpwm_capture_channel_e
+{
+  MCPWM_CAP_CHANNEL_0,
+  MCPWM_CAP_CHANNEL_1,
+  MCPWM_CAP_CHANNEL_2,
+  MCPWM_CAP_CHANNEL_MAX
+};
+
+struct mcpwm_dev_common_s
+{
+  mcpwm_hal_init_config_t group;
+  mcpwm_hal_context_t hal;
+  spinlock_t mcpwm_spinlock;
+  bool initialized;          /* MCPWM periph. and HAL has been initialized */
+  bool isr_initialized;      /* Shared ISR has been initialized */
+  int group_prescale;
+};
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+
+struct mcpwm_motor_lowerhalf_s
+{
+  /* The following block is part of the upper-half device struct */
+
+  FAR const struct motor_ops_s *ops;    /* Arch-specific operations */
+  uint8_t                      opmode;  /* Motor operation mode */
+  uint8_t                      opflags; /* Motor operation flags */
+  struct motor_limits_s        limits;  /* Motor absolute limits */
+  struct motor_params_s        param;   /* Motor settings */
+  struct motor_state_s         state;   /* Motor state */
+  FAR void                     *priv;   /* Private data */
+
+  /* The following is private to the ESP MCPWM driver */
+
+  struct mcpwm_dev_common_s *common;
+  mcpwm_timer_e             timer_id;
+  mcpwm_motor_channel_e     channel_id;
+  mcpwm_operator_e          operator_id;
+  uint32_t                  pwm_frequency;
+  uint16_t                  counter_peak;
+  uint8_t                   timer_prescale;
+  int                       fault_pin;  /* GPIO Pin for fault detection */
+  int                       generator_pins[MCPWM_GENERATOR_MAX];
+#ifdef ESP_MCPMW_MOTOR_FAULT
+  mcpwm_fault_e             fault_id;
+#endif
+};
+#endif /* CONFIG_ESP_MCPWM_MOTOR */
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
+/* Capture event data. The 'last_' value is used to calculate frequency */
+
+struct mcpwm_capture_event_data_s
+{
+  uint32_t pos_edge_count;
+  uint32_t neg_edge_count;
+  uint32_t last_pos_edge_count;
+};
+
+/* Lowe-half data structure for a capture channel */
+
+struct mcpwm_cap_channel_lowerhalf_s
+{
+  /* The following block is part of the upper-half device struct */
+
+  const struct cap_ops_s *ops;
+
+  /* The following is private to the ESP MCPWM driver */
+
+  struct mcpwm_dev_common_s *common;
+  struct mcpwm_capture_event_data_s *data;
+  int channel_id;
+  int gpio_pin;
+  uint32_t clock;
+  uint32_t freq;
+  uint8_t duty;
+  uint8_t isr_count;
+  bool ready;
+  bool enabled;
+};
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* General use for MCPWM peripheral */
+
+static void esp_mcpwm_group_start(void);
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
+static int esp_mcpwm_capture_set_gpio(
+  struct mcpwm_cap_channel_lowerhalf_s *lower);
+
+/* Lower half methods required by capture driver */
+
+static int esp_capture_start(struct cap_lowerhalf_s *lower);
+static int esp_capture_stop(struct cap_lowerhalf_s *lower);
+static int esp_capture_getduty(struct cap_lowerhalf_s *lower,
+                               uint8_t *duty);
+static int esp_capture_getfreq(struct cap_lowerhalf_s *lower,
+                               uint32_t *freq);
+#endif
+
+/* MCPWM Motor Control */
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+
+/* Upper-half functions required by motor driver */
+
+static int esp_motor_setup(struct motor_lowerhalf_s *dev);
+static int esp_motor_shutdown(struct motor_lowerhalf_s *dev);
+static int esp_motor_stop(struct motor_lowerhalf_s *dev);
+static int esp_motor_start(struct motor_lowerhalf_s *dev);
+static int esp_motor_mode_set(struct motor_lowerhalf_s *dev, uint8_t mode);
+static int esp_motor_fault_set(struct motor_lowerhalf_s *dev, uint8_t fault);
+static int esp_motor_fault_get(struct motor_lowerhalf_s *dev,
+                               uint8_t *fault);
+static int esp_motor_params_set(struct motor_lowerhalf_s *dev,
+                                struct motor_params_s *param);
+static int esp_motor_state_get(struct motor_lowerhalf_s *dev,
+                               struct motor_state_s *state);
+static int esp_motor_limits_set(struct motor_lowerhalf_s *dev,
+                                struct motor_limits_s *limits);
+static int esp_motor_fault_clear(struct motor_lowerhalf_s *dev,
+                                 uint8_t fault);
+static int esp_motor_ioctl(struct motor_lowerhalf_s *dev, int cmd,
+                           unsigned long arg);
+
+/* Lower-half motor functions */
+
+static int esp_motor_pwm_config(struct mcpwm_motor_lowerhalf_s *lower);
+static int esp_mcpwm_motor_set_gpio(
+  struct mcpwm_motor_lowerhalf_s *lower, bool enable);
+static int esp_motor_set_duty_cycle(
+  struct mcpwm_motor_lowerhalf_s *lower, float duty);
+static int esp_motor_bdc_set_direction(
+  struct mcpwm_motor_lowerhalf_s *lower);
+#endif
+
+/* MCPWM Fault Control */
+
+#ifdef ESP_MCPMW_MOTOR_FAULT
+static int esp_mcpwm_fault_gpio_config(
+  struct mcpwm_motor_lowerhalf_s *lower, bool enable);
+static int esp_motor_fault_configure(
+  struct mcpwm_motor_lowerhalf_s *lower, bool enable);
+#endif
+
+/* MCPWM Interrupt */
+
+#if defined(CONFIG_ESP_MCPWM_CAPTURE) || defined(ESP_MCPMW_MOTOR_FAULT)
+static int esp_mcpwm_isr_register(int (*fn)(int, void *, void *), void *arg);
+static int IRAM_ATTR mcpwm_driver_isr_default(int irq, void *context,
+                                              void *arg);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Common MCPWM data structure */
+
+static struct mcpwm_dev_common_s g_mcpwm_common =
+{
+  .group.group_id      = MCPWM_CAPTURE_DEFAULT_GROUP,
+  .initialized         = false,
+  .isr_initialized     = false,
+  .group_prescale      = MCPWM_DEV_CLK_PRESCALE,
+};
+
+/* Motor specific data structures */
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static const struct motor_ops_s mcpwm_motor_ops =
+{
+  .setup       = esp_motor_setup,
+  .shutdown    = esp_motor_shutdown,
+  .stop        = esp_motor_stop,
+  .start       = esp_motor_start,
+  .params_set  = esp_motor_params_set,
+  .mode_set    = esp_motor_mode_set,
+  .limits_set  = esp_motor_limits_set,
+  .fault_set   = esp_motor_fault_set,
+  .state_get   = esp_motor_state_get,
+  .fault_get   = esp_motor_fault_get,
+  .fault_clear = esp_motor_fault_clear,
+  .ioctl       = esp_motor_ioctl,
+};
+
+#if defined(CONFIG_ESP_MCPWM_MOTOR_CH0) &&\
+    defined(CONFIG_ESP_MCPWM_MOTOR_BDC)
+static struct mcpwm_motor_lowerhalf_s mcpwm_bdc_ch0_lowerhalf =
+{
+  .ops          = &mcpwm_motor_ops,
+  .common       = &g_mcpwm_common,
+  .channel_id   = MCPWM_MOTOR_CHANNEL_0,
+  .timer_id     = MCPWM_TIMER_0,
+  .operator_id  = MCPWM_OPERATOR_0,
+  .counter_peak = PEAK_COUNTER,
+#ifdef ESP_MCPMW_MOTOR_FAULT
+  .fault_id     = MCPWM_FAULT_0,
+#endif
+};
+#endif /* CONFIG_ESP_MCPWM_MOTOR_BDC_CH0 && CONFIG_ESP_MCPWM_MOTOR_BDC */
+#endif /* CONFIG_ESP_MCPWM_MOTOR */
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
+/* Lower half methods required by the capture driver */
+
+static const struct cap_ops_s mcpwm_cap_ops =
+{
+  .start   = esp_capture_start,
+  .stop    = esp_capture_stop,
+  .getduty = esp_capture_getduty,
+  .getfreq = esp_capture_getfreq,
+};
+
+/* Data structures for the available capture channels */
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE_CH0
+static struct mcpwm_capture_event_data_s event_data_ch0;
+static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch0_lowerhalf =
+{
+  .ops        = &mcpwm_cap_ops,
+  .common     = &g_mcpwm_common,
+  .data       = &event_data_ch0,
+  .channel_id = MCPWM_CAP_CHANNEL_0,
+  .ready      = false,
+};
+#endif
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE_CH1
+static struct mcpwm_capture_event_data_s event_data_ch1;
+static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch1_lowerhalf =
+{
+  .ops        = &mcpwm_cap_ops,
+  .common     = &g_mcpwm_common,
+  .data       = &event_data_ch1,
+  .channel_id = MCPWM_CAP_CHANNEL_1,
+  .ready      = false,
+};
+#endif
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE_CH2
+static struct mcpwm_capture_event_data_s event_data_ch2;
+static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch2_lowerhalf =
+{
+  .ops        = &mcpwm_cap_ops,
+  .common     = &g_mcpwm_common,
+  .data       = &event_data_ch2,
+  .channel_id = MCPWM_CAP_CHANNEL_2,
+  .ready      = false,
+};
+#endif
+#endif  /* CONFIG_ESP_MCPWM_CAPTURE */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp_motor_setup
+ *
+ * Description:
+ *   Configures the MCPWM operator and generator, setting the PWM clock and
+ *   output pins.
+ *   If required, also configures fault detection. When done, sets the the
+ *   motor state to IDLE.
+ *
+ * Input Parameters:
+ *   dev - Pointer to the motor channel lower-half data structure.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_setup(struct motor_lowerhalf_s *dev)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *)dev;
+  mcpwm_hal_context_t *hal = &priv->common->hal;
+  uint32_t base_clock;
+  irqstate_t flags;
+  int ret;
+
+  flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock);
+  if ((priv->state.state == MOTOR_STATE_FAULT) ||
+      (priv->state.state == MOTOR_STATE_CRITICAL))
+    {
+      mtrerr("Motor is in fault state. Clear faults first\n");
+      return ERROR;
+    }
+
+  mtrinfo("State: %d\n", priv->state.state);
+
+  esp_clk_tree_src_get_freq_hz(MCPWM_DEV_CLK_SOURCE,
+                               ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
+                               &base_clock);
+
+  mcpwm_hal_timer_reset(hal, priv->timer_id);
+  mcpwm_hal_operator_reset(hal, priv->operator_id);
+
+  /* Setup control in UP-DOWN mode for duty cycle control
+   * Peak value modifies the maximum and minimum possible frequencies.
+   * Important: the HAL subtracts 1 from the target value.
+   */
+
+  priv->timer_prescale = \
+    base_clock / ((2 * priv->counter_peak + 1) * priv->pwm_frequency);
+
+  mcpwm_ll_timer_set_peak(hal->dev, priv->timer_id,
+                          priv->counter_peak, true);
+  mcpwm_ll_timer_set_clock_prescale(hal->dev, priv->timer_id,
+                                    priv->timer_prescale);
+  mcpwm_ll_timer_update_period_at_once(hal->dev, priv->timer_id);
+  mcpwm_ll_timer_enable_update_period_on_tez(hal->dev, priv->timer_id, true);
+  mcpwm_ll_timer_set_count_mode(hal->dev, priv->timer_id,
+                                MCPWM_TIMER_COUNT_MODE_UP_DOWN);
+
+  mcpwm_ll_operator_flush_shadow(hal->dev, priv->operator_id);
+  mcpwm_ll_operator_connect_timer(hal->dev, priv->operator_id,
+                                  priv->timer_id);
+  mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, priv->operator_id,
+                                                 MCPWM_GENERATOR_0, true);
+
+#ifdef ESP_MCPMW_MOTOR_FAULT
+  esp_motor_fault_configure(priv, true);
+#endif
+  priv->state.state = MOTOR_STATE_IDLE;
+
+  spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags);
+  mtrinfo("Channel %d starts: prescale %d | freq: %"PRIu32"\n",
+          priv->channel_id, priv->timer_prescale - 1, priv->pwm_frequency);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_shutdown
+ *
+ * Description:
+ *   Stop the PWM timer and disable output on GPIO matrix.
+ *
+ * Input Parameters:
+ *   dev - Pointer to the motor channel lower-half data structure.
+ *
+ * Returned Value:
+ *   Returns OK on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_shutdown(struct motor_lowerhalf_s *dev)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *)dev;
+  mcpwm_hal_context_t *hal = &priv->common->hal;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock);
+
+  /* Stop the motor */
+
+  esp_motor_stop(dev);
+
+  /* Stop the PWM timer */
+
+  mcpwm_ll_timer_set_count_mode(hal->dev, priv->timer_id,
+                                MCPWM_TIMER_COUNT_MODE_PAUSE);
+  mcpwm_ll_timer_set_start_stop_command(hal->dev, priv->timer_id,
+                                        MCPWM_TIMER_STOP_EMPTY);
+
+  /* Disable fault detection */
+
+#ifdef ESP_MCPMW_MOTOR_FAULT
+  esp_motor_fault_configure(priv, false);
+#endif
+
+  spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_stop
+ *
+ * Description:
+ *   Holds the motor at a stand-still. PWM_A and PWM_B are kept low.
+ *
+ * Input Parameters:
+ *   dev - Pointer to the motor channel lower-half data structure.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_stop(struct motor_lowerhalf_s *dev)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *)dev;
+  mcpwm_hal_context_t *hal = &priv->common->hal;
+  irqstate_t flags;
+  int ret;
+
+  flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock);
+
+  if (priv->state.state == MOTOR_STATE_IDLE)
+    {
+      mtrerr("Motor already stopped\n");
+      return -EPERM;
+    }
+
+  mcpwm_ll_timer_set_start_stop_command(hal->dev, priv->timer_id,
+                                        MCPWM_TIMER_STOP_EMPTY);
+  mcpwm_ll_gen_set_continue_force_level(hal->dev,
+                                        priv->operator_id,
+                                        MCPWM_GENERATOR_0, 0);
+  mcpwm_ll_gen_set_continue_force_level(hal->dev,
+                                        priv->operator_id,
+                                        MCPWM_GENERATOR_1, 0);
+
+  ret = esp_motor_set_duty_cycle(priv, 0.0);
+  if (ret < 0)
+    {
+      mtrerr("Failed setting duty cycle to 0 on stop: %d\n", ret);
+      return ret;
+    }
+
+  if ((priv->state.state == MOTOR_STATE_FAULT) ||
+      (priv->state.state == MOTOR_STATE_CRITICAL))
+    {
+      mtrinfo("Channel %d stopped in fault state\n", priv->channel_id);
+    }
+  else
+    {
+      priv->state.state = MOTOR_STATE_IDLE;
+    }
+
+  priv->param.lock = false;
+  spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags);
+  mtrinfo("Channel %d stopped\n", priv->channel_id);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_start
+ *
+ * Description:
+ *   Start the motor by disabling forced actions and setting the duty cycle.
+ *   The motor parameters must have been set before calling this function.
+ *
+ * Input Parameters:
+ *   dev - Pointer to the motor channel lower-half data structure.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_start(struct motor_lowerhalf_s *dev)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *)dev;
+  mcpwm_hal_context_t *hal = &priv->common->hal;
+  irqstate_t flags;
+  int ret;
+  float duty;
+
+  flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock);
+  if (priv->state.state == MOTOR_STATE_RUN)
+    {
+      mtrerr("Motor already running\n");
+      return -EINVAL;
+    }
+
+  if ((priv->state.state == MOTOR_STATE_CRITICAL) ||
+       (priv->state.state == MOTOR_STATE_FAULT))
+    {
+      mtrerr("Motor is in fault state\n");
+      return -EINVAL;
+    }
+
+  ret = esp_motor_pwm_config(priv);
+  if (ret < 0)
+    {
+      mtrerr("Failed setting PWM configuration\n");
+      return ret;
+    }
+
+  /* Set motor direction */
+
+  esp_motor_bdc_set_direction(priv);
+  mcpwm_ll_timer_set_start_stop_command(hal->dev, priv->timer_id,
+                                        MCPWM_TIMER_START_NO_STOP);
+
+  /* Set duty cycle based on motor parameter and limits */
+
+#ifdef CONFIG_MOTOR_UPPER_HAVE_SPEED
+  if (priv->opmode == MOTOR_OPMODE_SPEED)
+    {
+      duty = priv->param.speed / priv->limits.speed;
+      ret = esp_motor_set_duty_cycle(priv, duty);
+      if (ret < 0)
+        {
+          mtrerr("Failed starting motor\n");
+          return ret;
+        }
+    }
+#endif
+
+  priv->state.state = MOTOR_STATE_RUN;
+  spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags);
+  mtrinfo("Motor start\n");
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_params_set
+ *
+ * Description:
+ *   Set parameters to run the motor.
+ *
+ * Input Parameters:
+ *   dev   - Pointer to the motor channel lower-half data structure.
+ *   param - Pointer to the motor parameter structure.
+ *
+ * Returned Value:
+ *   Returns OK on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_params_set(struct motor_lowerhalf_s *dev,
+  struct motor_params_s *param)
+{
+  DEBUGASSERT(dev != NULL);
+  DEBUGASSERT(param != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+#ifdef CONFIG_MOTOR_UPPER_HAVE_POSITION
+  priv->param.position = param->position;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_SPEED
+  priv->param.speed = param->speed;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_TORQUE
+  priv->param.torque = param->torque;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_FORCE
+  priv->param.force = param->force;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_ACCELERATION
+  priv->param.acceleration = param->acceleration;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_DECELERATION
+  priv->param.deceleration = param->deceleration;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_DIRECTION
+  priv->param.direction = param->direction;
+#endif
+  priv->param.lock = false;
+
+  /* Refresh duty and direction on the go */
+
+  if (priv->state.state == MOTOR_STATE_RUN)
+    {
+      float duty = priv->param.speed / priv->limits.speed;
+      esp_motor_set_duty_cycle(priv, duty);
+      esp_motor_bdc_set_direction(priv);
+    }
+
+  mtrinfo("Motor parameters set\n");
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_mode_set
+ *
+ * Description:
+ *   Sets the motor operating mode.
+ *
+ * Input Parameters:
+ *   dev  - Pointer to the motor channel lower-half data structure.
+ *   mode - Must be one of the motor_opmode_e enum.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_mode_set(struct motor_lowerhalf_s *dev, uint8_t mode)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+  if (mode > MOTOR_OPMODE_PATTERN)
+    {
+      mtrerr("Invalid operation mode: %u\n", mode);
+      return -EINVAL;
+    }
+
+  priv->opmode = mode;
+  mtrinfo("Mode set: %u\n", priv->opmode);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_limits_set
+ *
+ * Description:
+ *   Set motor limits. Must be called before start the motor.
+ *
+ * Input Parameters:
+ *   dev    - Pointer to the motor channel lower-half data structure.
+ *   limits - Pointer to the motor limits data structure.
+ *
+ * Returned Value:
+ *   Returns OK on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_limits_set(struct motor_lowerhalf_s *dev,
+  struct motor_limits_s *limits)
+{
+  DEBUGASSERT(dev != NULL);
+  DEBUGASSERT(limits != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+#ifdef CONFIG_MOTOR_UPPER_HAVE_POSITION
+  priv->limits.position = limits->position;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_SPEED
+  priv->limits.speed = limits->speed;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_TORQUE
+  priv->limits.torque = limits->torque;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_FORCE
+  priv->limits.force = limits->force;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_ACCELERATION
+  priv->limits.acceleration = limits->acceleration;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_DECELERATION
+  priv->limits.deceleration = limits->deceleration;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_INPUT_VOLTAGE
+  priv->limits.v_in = limits->v_in;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_INPUT_CURRENT
+  priv->limits.i_in = limits->i_in;
+#endif
+#ifdef CONFIG_MOTOR_UPPER_HAVE_INPUT_POWER
+  priv->limits.p_in = limits->p_in;
+#endif
+  priv->limits.lock = true;
+  mtrinfo("limits set and locked %d\n", priv->limits.lock);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_fault_set
+ *
+ * Description:
+ *   Sets the fault state for the motor.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the motor channel lower-half data structure.
+ *   fault - Fault value. Must be one of motor_fault_e enum.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_fault_set(struct motor_lowerhalf_s *dev, uint8_t fault)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+  if (fault > MOTOR_FAULT_OTHER)
+    {
+      mtrerr("Invalid fault value: %u\n", fault);
+      return -EINVAL;
+    }
+
+  priv->state.state = MOTOR_STATE_FAULT;
+  priv->state.fault |= fault;
+  mtrinfo("%u\n", priv->state.fault);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_state_get
+ *
+ * Description:
+ *   Get the current motor state.
+ *
+ * Input Parameters:
+ *   dev   - Pointer to the motor channel lower-half data structure.
+ *   state - Pointer to the motor state data structure.
+ *
+ * Returned Value:
+ *   Returns OK on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_state_get(struct motor_lowerhalf_s *dev,
+  struct motor_state_s *state)
+{
+  DEBUGASSERT(dev != NULL);
+  DEBUGASSERT(state != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+  state->state = priv->state.state;
+  state->fault = priv->state.fault;
+  memcpy(&state->state, &priv->state, sizeof(struct motor_feedback_s));
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_fault_get
+ *
+ * Description:
+ *   Get current motor fault state.
+ *
+ * Input Parameters:
+ *   dev   - Pointer to the motor channel lower-half data structure.
+ *   fault - Fault state value. Must be one of motor_fault_e enum.
+ *
+ * Returned Value:
+ *   Returns OK on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_fault_get(struct motor_lowerhalf_s *dev, uint8_t *fault)
+{
+  DEBUGASSERT(dev != NULL);
+  DEBUGASSERT(fault != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+  *fault = priv->state.fault;
+  mtrinfo("%u\n", priv->state.fault);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_fault_clear
+ *
+ * Description:
+ *   Clears a motor fault.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the capture channel lower-half data structure.
+ *   fault - Fault state to clear (one of motor_fault_e enum).
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_fault_clear(struct motor_lowerhalf_s *dev,
+  uint8_t fault)
+{
+  DEBUGASSERT(dev != NULL);
+
+  struct mcpwm_motor_lowerhalf_s *priv = (
+    struct mcpwm_motor_lowerhalf_s *) dev;
+
+  if (fault > MOTOR_FAULT_OTHER)
+    {
+      mtrerr("Invalid fault value: %u\n", fault);
+      return -EINVAL;
+    }
+
+  priv->state.fault &= ~fault;
+  if (priv->state.fault == 0)
+    {
+      priv->state.state = MOTOR_STATE_IDLE;
+      mtrinfo("All faults clear\n");
+      return OK;
+    }
+
+  mtrinfo("Fault clear: %u\n", fault);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_ioctl
+ *
+ * Description:
+ *  Unused but required for upper-half motor driver.
+ *
+ * Input Parameters:
+ *   dev - Pointer to the motor channel lower-half data structure.
+ *   cmd - Custom IOCTL.
+ *   arg - Argument to be passed for custom IOCTL.
+ *
+ * Returned Value:
+ *   Returns 1.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_ioctl(struct motor_lowerhalf_s *dev, int cmd,
+                           unsigned long arg)
+{
+  return 1;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_fault_configure
+ *
+ * Description:
+ *   Configure fault detection for motor channel. Enables the interrupt
+ *   handling to set and clear fault state and sets brake action on fault
+ *   (holds PWM at low state) in one-shot mode.
+ *
+ * Input Parameters:
+ *   dev    - Pointer to the motor channel lower-half data structure.
+ *   enable - True to setup motor fault. False to disable fault detection.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef ESP_MCPMW_MOTOR_FAULT
+static int esp_motor_fault_configure(struct mcpwm_motor_lowerhalf_s *lower,
+  bool enable)
+{
+  DEBUGASSERT(lower != NULL);
+
+  irqstate_t flags;
+  mcpwm_hal_context_t *hal = &lower->common->hal;
+
+  flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock);
+  if (!enable)
+    {
+      mcpwm_ll_fault_enable_detection(hal->dev, lower->fault_id, false);
+      mcpwm_ll_intr_enable(hal->dev,
+                           MCPWM_LL_EVENT_FAULT_ENTER(lower->fault_id),
+                           false);
+      mcpwm_ll_intr_enable(hal->dev,
+                           MCPWM_LL_EVENT_FAULT_EXIT(lower->fault_id),
+                           false);
+      return OK;
+    }
+
+  /* Detect fault when signal is high and also enable software trigger */
+
+  mcpwm_ll_fault_set_active_level(hal->dev, lower->fault_id, true);
+  mcpwm_ll_fault_enable_detection(hal->dev, lower->fault_id, true);
+  mcpwm_ll_brake_enable_oneshot_mode(hal->dev, lower->operator_id,
+                                     lower->fault_id, true);
+  mcpwm_ll_brake_enable_soft_ost(hal->dev, lower->operator_id, true);
+
+  /* Make sure the brake event can be triggered when the timer is
+   * counting up AND down because it is running in up-down mode.
+   */
+
+  mcpwm_ll_generator_set_action_on_brake_event(hal->dev,
+                                               lower->operator_id,
+                                               MCPWM_GENERATOR_0,
+                                               MCPWM_TIMER_DIRECTION_UP,
+                                               MCPWM_OPER_BRAKE_MODE_OST,
+                                               1);
+  mcpwm_ll_generator_set_action_on_brake_event(hal->dev,
+                                               lower->operator_id,
+                                               MCPWM_GENERATOR_0,
+                                               MCPWM_TIMER_DIRECTION_DOWN,
+                                               MCPWM_OPER_BRAKE_MODE_OST,
+                                               1);
+  mcpwm_ll_generator_set_action_on_brake_event(hal->dev,
+                                               lower->operator_id,
+                                               MCPWM_GENERATOR_1,
+                                               MCPWM_TIMER_DIRECTION_UP,
+                                               MCPWM_OPER_BRAKE_MODE_OST,
+                                               1);
+  mcpwm_ll_generator_set_action_on_brake_event(hal->dev,
+                                               lower->operator_id,
+                                               MCPWM_GENERATOR_1,
+                                               MCPWM_TIMER_DIRECTION_DOWN,
+                                               MCPWM_OPER_BRAKE_MODE_OST,
+                                               1);
+
+  /* Enable interrupt requests for motor fault */
+
+  mcpwm_ll_brake_clear_ost(hal->dev, lower->operator_id);
+  mcpwm_ll_intr_enable(hal->dev,
+                       MCPWM_LL_EVENT_FAULT_ENTER(lower->fault_id),
+                       true);
+  mcpwm_ll_intr_enable(hal->dev,
+                       MCPWM_LL_EVENT_FAULT_EXIT(lower->fault_id),
+                       true);
+  mcpwm_ll_intr_clear_status(hal->dev,
+                             MCPWM_LL_EVENT_FAULT_ENTER(lower->fault_id));
+  mcpwm_ll_intr_clear_status(hal->dev,
+                             MCPWM_LL_EVENT_FAULT_EXIT(lower->fault_id));
+
+  spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags);
+  mtrinfo("Brake configured for motor channel %d on fault id %d",
+          lower->channel_id, lower->fault_id);
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_set_duty_cycle
+ *
+ * Description:
+ *   Sets the duty cycle on output PWM_A and PWM_B.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the motor channel lower-half data structure.
+ *   duty  - Duty-cycle value from 0.0 to 1.0.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_set_duty_cycle(struct mcpwm_motor_lowerhalf_s *lower,
+  float duty)
+{
+  DEBUGASSERT(lower != NULL);
+
+  mcpwm_hal_context_t *hal = &lower->common->hal;
+
+  if (duty < 0.0 || duty > 1.0)
+    {
+      mtrerr("Invalid duty cycle value: %f\n", duty);
+      return -EINVAL;
+    }
+
+  uint32_t pwm_count = -1 * lower->counter_peak * (duty - 1.0);
+  mcpwm_ll_operator_set_compare_value(hal->dev, lower->operator_id,
+                                      MCPWM_GENERATOR_0, pwm_count);
+  mtrinfo("Duty %f compare value set: %lu\n", duty, pwm_count);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: esp_motor_bdc_set_direction
+ *
+ * Description:
+ *   Sets direction of motor spin by disabling PWM_A or PWM_B. If
+ *   CONFIG_MOTOR_UPPER_HAVE_DIRECTION is not defined, defaults to CW.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the motor channel lower-half data structure.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_bdc_set_direction(struct mcpwm_motor_lowerhalf_s *lower)
 {
-  mcpwm_hal_init_config_t group;
-  mcpwm_hal_context_t hal;
-  spinlock_t mcpwm_spinlock;
-  bool initialized;          /* MCPWM peripheral and HAL has been initialized 
*/
-  bool capture_initialized;  /* Capture submodule has been initialized */
-  int group_prescale;
-};
+  DEBUGASSERT(lower != NULL);
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
-/* Capture event data. The 'last_' value is used to calculate frequency */
+  mcpwm_hal_context_t *hal = &lower->common->hal;
+  int8_t direction;
 
-struct mcpwm_capture_event_data_s
-{
-  uint32_t pos_edge_count;
-  uint32_t neg_edge_count;
-  uint32_t last_pos_edge_count;
-};
+  if (lower->opmode == MOTOR_OPMODE_SPEED)
+    {
+#ifdef CONFIG_MOTOR_UPPER_HAVE_DIRECTION
+      if (lower->param.direction == MOTOR_DIR_CW)
+        {
+          mcpwm_ll_gen_disable_continue_force_action(hal->dev,
+                                                     lower->operator_id,
+                                                     MCPWM_GENERATOR_0);
+          mcpwm_ll_gen_set_continue_force_level(hal->dev,
+                                                lower->operator_id,
+                                                MCPWM_GENERATOR_1, 0);
+        }
+      else
+        {
+          mcpwm_ll_gen_disable_continue_force_action(hal->dev,
+                                                     lower->operator_id,
+                                                     MCPWM_GENERATOR_1);
+          mcpwm_ll_gen_set_continue_force_level(hal->dev,
+                                                lower->operator_id,
+                                                MCPWM_GENERATOR_0, 0);
+        }
 
-enum mcpwm_capture_channel_e
-{
-  MCPWM_CAP_CHANNEL_0,  /* MCPWM capture channel number 0 */
-  MCPWM_CAP_CHANNEL_1,  /* MCPWM capture channel number 1 */
-  MCPWM_CAP_CHANNEL_2,  /* MCPWM capture channel number 2 */
-  MCPWM_CAP_CHANNEL_MAX /* Number of MCPWM capture channels */
-};
+      direction = lower->param.direction;
+#else
+      mcpwm_ll_gen_disable_continue_force_action(hal->dev,
+                                                  lower->operator_id,
+                                                  MCPWM_GENERATOR_0);
+      mcpwm_ll_gen_set_continue_force_level(hal->dev,
+                                            lower->operator_id,
+                                            MCPWM_GENERATOR_1, 0);
+      direction = MOTOR_DIR_CW;
+#endif
+    }
 
-/* Lowe-half data structure for a capture channel */
+  mtrinfo("Motor direction set: %d\n", direction);
+  return OK;
+}
+#endif
 
-struct mcpwm_cap_channel_lowerhalf_s
+/****************************************************************************
+ * Name: esp_motor_pwm_config
+ *
+ * Description:
+ *  Configures the control waveform by setting some configurations for
+ *  timer events. Included configurations are: speed control.
+ *
+ * Input Parameters:
+ *   lower - Pointer to the motor channel lower-half data structure.
+ *
+ * Returned Value:
+ *   Returns OK on success.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_motor_pwm_config(struct mcpwm_motor_lowerhalf_s *lower)
 {
-  /* The following block is part of the upper-half device struct */
+  mcpwm_hal_context_t *hal = &lower->common->hal;
 
-  const struct cap_ops_s *ops;
+  /* Configure PWM_A and PWM_B as complementary */
 
-  /* The following is private to the ESP MCPWM driver */
+  if (lower->opmode == MOTOR_OPMODE_SPEED)
+    {
+      /* PWM_A Output */
+
+      mcpwm_ll_generator_set_action_on_compare_event(hal->dev,
+        lower->operator_id,
+        MCPWM_GENERATOR_0,
+        MCPWM_TIMER_DIRECTION_UP,
+        MCPWM_GENERATOR_0,
+        MCPWM_GEN_ACTION_HIGH);
+      mcpwm_ll_generator_set_action_on_compare_event(
+        hal->dev,
+        lower->operator_id,
+        MCPWM_GENERATOR_0,
+        MCPWM_TIMER_DIRECTION_DOWN,
+        MCPWM_GENERATOR_0,
+        MCPWM_GEN_ACTION_LOW);
+
+      /* PWM_B Output */
+
+      mcpwm_ll_generator_set_action_on_compare_event(hal->dev,
+        lower->operator_id,
+        MCPWM_GENERATOR_1,
+        MCPWM_TIMER_DIRECTION_UP,
+        MCPWM_GENERATOR_0,
+        MCPWM_GEN_ACTION_HIGH);
+      mcpwm_ll_generator_set_action_on_compare_event(hal->dev,
+        lower->operator_id,
+        MCPWM_GENERATOR_1,
+        MCPWM_TIMER_DIRECTION_DOWN,
+        MCPWM_GENERATOR_0,
+        MCPWM_GEN_ACTION_LOW);
+    }
+  else
+    {
+      mtrerr("Invalid operation mode\n");
+      return -EPERM;
+    }
 
-  struct mcpwm_dev_common_s *common;
-  struct mcpwm_capture_event_data_s *data;
-  int channel_id;
-  int gpio_pin;
-  uint32_t clock;
-  uint32_t freq;
-  uint8_t duty;
-  uint8_t isr_count;
-  bool ready;
-  bool enabled;
-};
+  return OK;
+}
 #endif
 
 /****************************************************************************
- * Private Function Prototypes
+ * Name: esp_mcpwm_fault_configure
+ *
+ * Description:
+ *   Configures the fault GPIO.
+ *
+ * Input Parameters:
+ *   lower  - Pointer to the motor channel lower-half data structure.
+ *   enable - True to configure motor fault. False to disable.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
  ****************************************************************************/
 
-static void esp_mcpwm_group_start(void);
+#ifdef ESP_MCPMW_MOTOR_FAULT
+static int esp_mcpwm_fault_gpio_config(struct mcpwm_motor_lowerhalf_s *lower,
+  bool enable)
+{
+  int ret;
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
-static int esp_mcpwm_capture_set_gpio(
-  struct mcpwm_cap_channel_lowerhalf_s *lower);
-static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *),
-                                          void *arg);
-static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context,
-                                                      void *arg);
+  if (!enable)
+    {
+      esp_gpio_matrix_in(0x3a,
+        mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\
+        gpio_faults[lower->fault_id].fault_sig,
+        false);
+      return OK;
+    }
 
-/* Lower half methods required by capture driver */
+  ret = esp_configgpio(lower->fault_pin, INPUT_PULLDOWN | GPIO_IN_FUNCTION);
+  if (ret < 0)
+    {
+      mtrerr("Failed configuring fault GPIO\n");
+      return ret;
+    }
 
-static int esp_capture_start(struct cap_lowerhalf_s *lower);
-static int esp_capture_stop(struct cap_lowerhalf_s *lower);
-static int esp_capture_getduty(struct cap_lowerhalf_s *lower,
-                               uint8_t *duty);
-static int esp_capture_getfreq(struct cap_lowerhalf_s *lower,
-                               uint32_t *freq);
+  esp_gpio_matrix_in(
+    lower->fault_pin,
+    mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\
+    gpio_faults[lower->fault_id].fault_sig,
+    false);
+
+  mtrinfo("Fault signal configured for GPIO %d in channel %d\n",
+          lower->fault_pin, lower->channel_id);
+
+  return ret;
+}
 #endif
 
 /****************************************************************************
- * Private Data
+ * Name: esp_mcpwm_motor_set_gpio
+ *
+ * Description:
+ *   Configures the GPIO pins to be used as motor PWM output and the fault
+ *   GPIO (if enabled).
+ *
+ * Input Parameters:
+ *   lower  - Pointer to the motor channel lower-half data structure.
+ *   enable - True to configure GPIO for motor PWM. False to disable.
+ *
+ * Returned Value:
+ *   OK on success, otherwise a negated errno value is returned on
+ *   any failure.
+ *
  ****************************************************************************/
 
-static struct mcpwm_dev_common_s mcpwm_common =
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+static int esp_mcpwm_motor_set_gpio(struct mcpwm_motor_lowerhalf_s *lower,
+  bool enable)
 {
-  .group.group_id      = MCPWM_CAPTURE_DEFAULT_GROUP,
-  .initialized         = false,
-  .capture_initialized = false,
-  .group_prescale      = MCPWM_DEV_CLK_PRESCALE,
-};
+  int ret;
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
-/* Lower half methods required by the capture driver */
+  if (!enable)
+    {
+      esp_gpio_matrix_out(lower->generator_pins[MCPWM_GENERATOR_0], 0x100,
+                          false, false);
 
-static const struct cap_ops_s mcpwm_cap_ops =
-{
-  .start   = esp_capture_start,
-  .stop    = esp_capture_stop,
-  .getduty = esp_capture_getduty,
-  .getfreq = esp_capture_getfreq,
-};
+      esp_gpio_matrix_out(lower->generator_pins[MCPWM_GENERATOR_1], 0x100,
+                          false, false);
+      return OK;
+    }
 
-/* Data structures for the available capture channels */
+  ret = esp_configgpio(lower->generator_pins[MCPWM_GENERATOR_0],
+                       GPIO_OUT_FUNCTION);
+  if (ret < 0)
+    {
+      mtrerr("Failed configuring PWM_A GPIO\n");
+      return ret;
+    }
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE_CH0
-static struct mcpwm_capture_event_data_s event_data_ch0;
-static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch0_lowerhalf =
-{
-  .ops        = &mcpwm_cap_ops,
-  .common     = &mcpwm_common,
-  .data       = &event_data_ch0,
-  .channel_id = MCPWM_CAP_CHANNEL_0,
-  .ready      = false,
-};
-#endif
+  ret = esp_configgpio(lower->generator_pins[MCPWM_GENERATOR_1],
+                       GPIO_OUT_FUNCTION);
+  if (ret < 0)
+    {
+      mtrerr("Failed configuring PWM_B GPIO\n");
+      return ret;
+    }
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE_CH1
-static struct mcpwm_capture_event_data_s event_data_ch1;
-static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch1_lowerhalf =
-{
-  .ops        = &mcpwm_cap_ops,
-  .common     = &mcpwm_common,
-  .data       = &event_data_ch1,
-  .channel_id = MCPWM_CAP_CHANNEL_1,
-  .ready      = false,
-};
+  esp_gpio_matrix_out(
+    lower->generator_pins[MCPWM_GENERATOR_0],
+    mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\
+    operators[lower->channel_id].generators[MCPWM_GENERATOR_0].pwm_sig,
+    false, false);
+
+  esp_gpio_matrix_out(
+    lower->generator_pins[MCPWM_GENERATOR_1],
+    mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\
+    operators[lower->channel_id].generators[MCPWM_GENERATOR_1].pwm_sig,
+    false, false);
+
+  /* Connects the PWM output to the Capture input */
+
+#ifdef CONFIG_ESP_MCPWM_TEST_LOOPBACK
+  esp_gpio_matrix_out(CONFIG_ESP_MCPWM_CAPTURE_CH0_GPIO,
+                      mcpwm_periph_signals.\
+                      groups[MCPWM_CAPTURE_DEFAULT_GROUP].\
+                      operators[lower->channel_id].\
+                      generators[MCPWM_GENERATOR_0].pwm_sig,
+                      0, 0);
+  esp_gpio_matrix_out(CONFIG_ESP_MCPWM_CAPTURE_CH1_GPIO,
+                      mcpwm_periph_signals.\
+                      groups[MCPWM_CAPTURE_DEFAULT_GROUP].\
+                      operators[lower->channel_id].\
+                      generators[MCPWM_GENERATOR_1].pwm_sig,
+                      0, 0);
+  mtrinfo("Loopback for capture device is enabled\n");
 #endif
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE_CH2
-static struct mcpwm_capture_event_data_s event_data_ch2;
-static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch2_lowerhalf =
-{
-  .ops        = &mcpwm_cap_ops,
-  .common     = &mcpwm_common,
-  .data       = &event_data_ch2,
-  .channel_id = MCPWM_CAP_CHANNEL_2,
-  .ready      = false,
-};
-#endif
-#endif  /* CONFIG_ESP_MCPWM_CAPTURE */
+  mtrinfo("GPIO: %d PWM_A configured for channel %d\n",
+         lower->generator_pins[MCPWM_GENERATOR_0],
+         lower->channel_id);
+  mtrinfo("GPIO: %d PWM_B configured for channel %d\n",
+         lower->generator_pins[MCPWM_GENERATOR_1],
+         lower->channel_id);
 
-/****************************************************************************
- * Private Functions
- ****************************************************************************/
+  return ret;
+}
+#endif
 
 /****************************************************************************
  * Name: esp_capture_start
@@ -226,15 +1416,17 @@ static struct mcpwm_cap_channel_lowerhalf_s 
mcpwm_cap_ch2_lowerhalf =
 #ifdef CONFIG_ESP_MCPWM_CAPTURE
 static int esp_capture_start(struct cap_lowerhalf_s *lower)
 {
+  DEBUGASSERT(lower != NULL);
+
   struct mcpwm_cap_channel_lowerhalf_s *priv = (
     struct mcpwm_cap_channel_lowerhalf_s *)lower;
-
-  DEBUGASSERT(priv != NULL);
-
+  irqstate_t flags;
   mcpwm_hal_context_t *hal = &priv->common->hal;
+  flags = spin_lock_irqsave(priv->common->mcpwm_spinlock);
 
   /* Enable channel and interruption for rising edge */
 
+  mcpwm_ll_capture_enable_timer(g_mcpwm_common.hal.dev, true);
   mcpwm_ll_capture_enable_channel(hal->dev, priv->channel_id, true);
   mcpwm_ll_intr_enable(hal->dev,
                        MCPWM_LL_EVENT_CAPTURE(priv->channel_id),
@@ -252,6 +1444,7 @@ static int esp_capture_start(struct cap_lowerhalf_s *lower)
   priv->enabled = true;
   priv->ready = false;
 
+  spin_unlock_irqrestore(priv->common->mcpwm_spinlock, flags);
   cpinfo("Channel enabled: %d\n", priv->channel_id);
   return OK;
 }
@@ -275,21 +1468,24 @@ static int esp_capture_start(struct cap_lowerhalf_s 
*lower)
 #ifdef CONFIG_ESP_MCPWM_CAPTURE
 static int esp_capture_stop(struct cap_lowerhalf_s *lower)
 {
+  DEBUGASSERT(lower != NULL);
+
   struct mcpwm_cap_channel_lowerhalf_s *priv = (
     struct mcpwm_cap_channel_lowerhalf_s *)lower;
-
-  DEBUGASSERT(priv != NULL);
-
+  irqstate_t flags;
   mcpwm_hal_context_t *hal = &priv->common->hal;
+  flags = spin_lock_irqsave(priv->common->mcpwm_spinlock);
 
   /* Disable channel and interrupts */
 
+  mcpwm_ll_capture_enable_timer(g_mcpwm_common.hal.dev, false);
   mcpwm_ll_capture_enable_channel(hal->dev, priv->channel_id, false);
   mcpwm_ll_intr_enable(hal->dev,
                        MCPWM_LL_EVENT_CAPTURE(priv->channel_id),
                        false);
   priv->enabled = false;
 
+  spin_unlock_irqrestore(priv->common->mcpwm_spinlock, flags);
   cpinfo("Channel disabled: %d\n", priv->channel_id);
   return OK;
 }
@@ -374,18 +1570,17 @@ static int esp_capture_getfreq(struct cap_lowerhalf_s 
*lower,
 
 static void esp_mcpwm_group_start(void)
 {
-  mcpwm_hal_context_t *hal = &mcpwm_common.hal;
+  mcpwm_hal_context_t *hal = &g_mcpwm_common.hal;
 
   /* HAL and MCPWM Initialization */
 
   periph_module_enable(PERIPH_MCPWM0_MODULE);
-  mcpwm_hal_init(hal, &mcpwm_common.group);
-  mcpwm_hal_timer_reset(hal, 0);
+  mcpwm_hal_init(hal, &g_mcpwm_common.group);
   mcpwm_ll_group_set_clock_source(hal->dev, MCPWM_DEV_CLK_SOURCE);
-  mcpwm_ll_group_set_clock_prescale(hal->dev, 4);
+  mcpwm_ll_group_set_clock_prescale(hal->dev, g_mcpwm_common.group_prescale);
   mcpwm_ll_group_enable_clock(hal->dev, true);
 
-  mcpwm_common.initialized = true;
+  g_mcpwm_common.initialized = true;
 }
 
 /****************************************************************************
@@ -428,7 +1623,7 @@ static int esp_mcpwm_capture_set_gpio(
 #endif
 
 /****************************************************************************
- * Name: esp_mcpwm_capture_isr_register
+ * Name: esp_mcpwm_isr_register
  *
  * Description:
  *   Registers a callback function for a channel interrupt request.
@@ -443,8 +1638,8 @@ static int esp_mcpwm_capture_set_gpio(
  *
  ****************************************************************************/
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
-static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *),
+#if defined(CONFIG_ESP_MCPWM_CAPTURE) || defined(ESP_MCPMW_MOTOR_FAULT)
+static int esp_mcpwm_isr_register(int (*fn)(int, void *, void *),
                                           void *arg)
 {
   int cpuint;
@@ -460,8 +1655,8 @@ static int esp_mcpwm_capture_isr_register(int (*fn)(int, 
void *, void *),
     }
 
   ret = irq_attach(ESP_IRQ_MCPWM0,
-                   mcpwm_capture_driver_isr_default,
-                   &mcpwm_common);
+                   fn,
+                   &g_mcpwm_common);
   if (ret < 0)
     {
       cperr("Couldn't attach IRQ to handler.\n");
@@ -476,7 +1671,7 @@ static int esp_mcpwm_capture_isr_register(int (*fn)(int, 
void *, void *),
 #endif
 
 /****************************************************************************
- * Name: mcpwm_capture_driver_isr_default
+ * Name: mcpwm_driver_isr_default
  *
  * Description:
  *   Default function called when a capture interrupt occurs.
@@ -499,21 +1694,28 @@ static int esp_mcpwm_capture_isr_register(int (*fn)(int, 
void *, void *),
  *
  ****************************************************************************/
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
-static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context,
-                                                      void *arg)
+#if defined(CONFIG_ESP_MCPWM_CAPTURE) || defined(ESP_MCPMW_MOTOR_FAULT)
+static int IRAM_ATTR mcpwm_driver_isr_default(int irq, void *context,
+                                              void *arg)
 {
   struct mcpwm_dev_common_s *common = (struct mcpwm_dev_common_s *)arg;
+  uint32_t status;
+  irqstate_t flags;
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
   struct mcpwm_cap_channel_lowerhalf_s *lower = NULL;
   struct mcpwm_capture_event_data_s *data = NULL;
-  irqstate_t flags;
   uint32_t cap_value;
-  uint32_t status;
   mcpwm_capture_edge_t cap_edge;
+#endif
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+  struct mcpwm_motor_lowerhalf_s *priv = NULL;
+#endif
 
   flags = spin_lock_irqsave(common->mcpwm_spinlock);
   status = mcpwm_ll_intr_get_status(common->hal.dev);
 
+  /* Evaluate capture interrupt for all 3 cap channels */
+
   if (status & MCPWM_LL_EVENT_CAPTURE(MCPWM_CAP_CHANNEL_0))
     {
 #ifdef CONFIG_ESP_MCPWM_CAPTURE_CH0
@@ -532,9 +1734,44 @@ static int IRAM_ATTR mcpwm_capture_driver_isr_default(int 
irq, void *context,
       lower = &mcpwm_cap_ch2_lowerhalf;
     }
 #endif
-  else
+
+  /* Evaluate fault interrupt which can be of the ENTER or EXIT type */
+
+#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT
+  if (status & MCPWM_LL_EVENT_FAULT_ENTER(MCPWM_FAULT_0))
+    {
+      priv = &mcpwm_bdc_ch0_lowerhalf;
+      mcpwm_ll_intr_clear_status(common->hal.dev,
+                                 status &
+                                 MCPWM_LL_EVENT_FAULT_ENTER(MCPWM_FAULT_0));
+      esp_motor_fault_set((struct motor_lowerhalf_s *)priv,
+                          MOTOR_FAULT_OTHER);
+      mcpwm_ll_brake_trigger_soft_ost(common->hal.dev, priv->operator_id);
+      mtrinfo("enter: %lu\n", status);
+    }
+
+  if (status & MCPWM_LL_EVENT_FAULT_EXIT(MCPWM_FAULT_0))
     {
-      return -ERANGE;
+      priv = &mcpwm_bdc_ch0_lowerhalf;
+      mcpwm_ll_intr_clear_status(common->hal.dev,
+                                 status &
+                                 MCPWM_LL_EVENT_FAULT_EXIT(MCPWM_FAULT_0));
+      esp_motor_fault_clear((struct motor_lowerhalf_s *)priv,
+                            MOTOR_FAULT_OTHER);
+      mcpwm_ll_brake_clear_ost(common->hal.dev, MCPWM_FAULT_0);
+      mtrinfo("exit: %lu\n", status);
+    }
+#endif
+
+  /* If capture is disabled or the interrupt was not related to it,
+   * simply return. Otherwise, continue executing capture math
+   */
+
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
+  if (lower == NULL)
+    {
+      spin_unlock_irqrestore(common->mcpwm_spinlock, flags);
+      return OK;
     }
 
   mcpwm_ll_intr_clear_status(common->hal.dev,
@@ -598,9 +1835,9 @@ static int IRAM_ATTR mcpwm_capture_driver_isr_default(int 
irq, void *context,
                                       lower->channel_id,
                                       true);
     }
+#endif
 
   spin_unlock_irqrestore(common->mcpwm_spinlock, flags);
-
   return OK;
 }
 #endif
@@ -609,6 +1846,95 @@ static int IRAM_ATTR mcpwm_capture_driver_isr_default(int 
irq, void *context,
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: esp_motor_bdc_initialize
+ *
+ * Description:
+ *   This function initializes the MCPWM peripheral and configures the
+ *   motor control driver.
+ *
+ * Input Parameters:
+ *   channel    - Channel to be initialized [0-3].
+ *   frequency  - PWM output frequency in Hz.
+ *   pwm_a_pin  - GPIO pin for PWM_A output.
+ *   pwm_b_pin  - GPIO pin for PWM_B output (complements PWM_A).
+ *   fault_pin  - Indicates input pin to detect fault (to be implemented).
+ *
+ * Returned Value:
+ *   On success, this function returns a valid pointer to the Capture device
+ *   structure. If the initialization fails, it returns NULL.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC
+struct motor_lowerhalf_s *esp_motor_bdc_initialize(int channel,
+  uint32_t frequency, int pwm_a_pin, int pwm_b_pin, int fault_pin)
+{
+  struct mcpwm_motor_lowerhalf_s *lower = NULL;
+  uint32_t ref_clock;
+  int ret;
+
+  if ((frequency > MCPWM_MAX_PWM_OUT_FREQ) ||
+      (frequency < MCPWM_MIN_PWM_OUT_FREQ))
+    {
+      mtrerr("Invalid frequency set. Must be between %d and %d Hz\n",
+             MCPWM_MIN_PWM_OUT_FREQ, MCPWM_MAX_PWM_OUT_FREQ);
+      return NULL;
+    }
+
+  if (!g_mcpwm_common.initialized)
+    {
+      esp_mcpwm_group_start();
+    }
+
+#ifdef ESP_MCPMW_MOTOR_FAULT
+  if (!g_mcpwm_common.isr_initialized)
+    {
+      esp_mcpwm_isr_register(mcpwm_driver_isr_default, &g_mcpwm_common);
+      g_mcpwm_common.isr_initialized = true;
+    }
+#endif
+
+  switch (channel)
+    {
+      case 0:
+        lower = &mcpwm_bdc_ch0_lowerhalf;
+        lower->pwm_frequency = frequency;
+        break;
+      default:
+        mtrerr("Invalid channel selection: %d\n", channel);
+        return NULL;
+    }
+
+  lower->generator_pins[MCPWM_GENERATOR_0] = pwm_a_pin;
+  lower->generator_pins[MCPWM_GENERATOR_1] = pwm_b_pin;
+  lower->fault_pin = fault_pin;
+
+  /* Configure GPIOs before starting */
+
+#ifdef ESP_MCPMW_MOTOR_FAULT
+  ret = esp_mcpwm_fault_gpio_config(lower, true);
+  if (ret < 0)
+    {
+      mtrerr("Failed configuring motor fault GPIOs\n");
+      return NULL;
+    }
+#endif
+
+  ret = esp_mcpwm_motor_set_gpio(lower, true);
+  if (ret < 0)
+    {
+      mtrerr("Failed configuring motor PWM GPIOs\n");
+      return NULL;
+    }
+
+  mtrinfo("Ch %d initialized. GPIO: PWM_A: %d | PWM_B: %d | Freq: %lu\n",
+          lower->channel_id, lower->generator_pins[MCPWM_GENERATOR_0],
+          lower->generator_pins[MCPWM_GENERATOR_1], lower->pwm_frequency);
+  return (struct motor_lowerhalf_s *) lower;
+}
+#endif
+
 /****************************************************************************
  * Name: esp_mcpwm_capture_initialize
  *
@@ -636,17 +1962,16 @@ struct cap_lowerhalf_s *esp_mcpwm_capture_initialize(int 
channel, int pin)
    * and MCPWM Capture group.
    */
 
-  if (!mcpwm_common.initialized)
+  if (!g_mcpwm_common.initialized)
     {
       esp_mcpwm_group_start();
     }
 
-  if (!mcpwm_common.capture_initialized)
+  if (!g_mcpwm_common.isr_initialized)
     {
-      mcpwm_ll_capture_enable_timer(mcpwm_common.hal.dev, true);
-      esp_mcpwm_capture_isr_register(mcpwm_capture_driver_isr_default,
-                                     &mcpwm_common);
-      mcpwm_common.capture_initialized = true;
+      esp_mcpwm_isr_register(mcpwm_driver_isr_default,
+                             &g_mcpwm_common);
+      g_mcpwm_common.isr_initialized = true;
     }
 
   switch (channel)
@@ -678,7 +2003,7 @@ struct cap_lowerhalf_s *esp_mcpwm_capture_initialize(int 
channel, int pin)
   /* Set the clock to be used when calculating frequency */
 
   lower->gpio_pin = pin;
-  lower->clock = group_clock / MCPWM_DEV_CLK_PRESCALE;
+  lower->clock = group_clock / g_mcpwm_common.group_prescale;
 
   /* Configure GPIO pin */
 
diff --git a/arch/risc-v/src/common/espressif/esp_mcpwm.h 
b/arch/risc-v/src/common/espressif/esp_mcpwm.h
index 946f7310d7..6177905d63 100644
--- a/arch/risc-v/src/common/espressif/esp_mcpwm.h
+++ b/arch/risc-v/src/common/espressif/esp_mcpwm.h
@@ -56,6 +56,31 @@ extern "C"
  * Public Function Prototypes
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: esp_motor_bdc_initialize
+ *
+ * Description:
+ *   This function initializes the MCPWM peripheral and configures the
+ *   motor control driver.
+ *
+ * Input Parameters:
+ *   channel   - Channel to be initialized (only 0 available for now).
+ *   frequency - PWM output frequency in Hertz.
+ *   pwm_a_pin - GPIO pin number for PWM0_A output.
+ *   pwm_b_pin - GPIO pin number for PWM0_B output.
+ *   fault_pin - GPIO pin number for fault signal input.
+ *
+ * Returned Value:
+ *   On success, this function returns a valid pointer to the Capture device
+ *   structure. If the initialization fails, it returns NULL.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC
+struct motor_lowerhalf_s *esp_motor_bdc_initialize(int channel,
+  uint32_t frequency, int pwm_a_pin, int pwm_b_pin, int fault_pin);
+#endif
+
 /****************************************************************************
  * Name: esp_mcpwm_capture_initialize
  *
diff --git a/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h 
b/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h
index 421fbffd3a..191deab98a 100644
--- a/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h
+++ b/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h
@@ -43,7 +43,24 @@ extern "C"
  * Public Function Prototypes
  ****************************************************************************/
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
+/****************************************************************************
+ * Name: board_motor_initialize
+ *
+ * Description:
+ *   Initialize MCPWM peripheral for motor control and register the motor
+ *   driver.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+int board_motor_initialize(void);
+#endif
 
 /****************************************************************************
  * Name: board_capture_initialize
@@ -59,9 +76,10 @@ extern "C"
  *
  ****************************************************************************/
 
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
 int board_capture_initialize(void);
+#endif
 
-#endif /* CONFIG_ESP_MCPWM_CAPTURE */
 #undef EXTERN
 #ifdef __cplusplus
 }
diff --git a/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c 
b/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c
index 5229de617c..43a059855a 100644
--- a/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c
+++ b/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c
@@ -29,7 +29,12 @@
 #include <debug.h>
 
 #include <nuttx/board.h>
+#ifdef CONFIG_MOTOR
+#include <nuttx/motor/motor.h>
+#endif
+#ifdef CONFIG_CAPTURE
 #include <nuttx/timers/capture.h>
+#endif
 
 #include <arch/board/board.h>
 
@@ -39,10 +44,61 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT
+#  define MCPWM_FAULT_GPIO CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT_GPIO
+#else
+#  define MCPWM_FAULT_GPIO 0
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: board_motor_initialize
+ *
+ * Description:
+ *   Initialize MCPWM peripheral for motor control and register the motor
+ *   driver.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+int board_motor_initialize(void)
+{
+  int ret;
+  struct motor_lowerhalf_s *motor;
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR_CH0
+  motor = esp_motor_bdc_initialize(0,
+                                   CONFIG_ESP_MCPWM_MOTOR_CH0_PWM_FREQ,
+                                   CONFIG_ESP_MCPWM_MOTOR_CH0_PWMA_GPIO,
+                                   CONFIG_ESP_MCPWM_MOTOR_CH0_PWMB_GPIO,
+                                   MCPWM_FAULT_GPIO);
+  if (motor == NULL)
+    {
+      syslog(LOG_ERR, "ERROR: Failed to start MCPWM BDC Motor: CH0\n");
+      return -ENODEV;
+    }
+
+  ret = motor_register("/dev/motor0", motor);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: motor_register failed: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Name: board_capture_initialize
  *
@@ -57,6 +113,7 @@
  *
  ****************************************************************************/
 
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
 int board_capture_initialize(void)
 {
   int ret;
@@ -112,3 +169,4 @@ int board_capture_initialize(void)
 
   return OK;
 }
+#endif
diff --git a/boards/risc-v/esp32c6/esp32c6-devkitc/configs/motor/defconfig 
b/boards/risc-v/esp32c6/esp32c6-devkitc/configs/motor/defconfig
new file mode 100644
index 0000000000..bf70eb967e
--- /dev/null
+++ b/boards/risc-v/esp32c6/esp32c6-devkitc/configs/motor/defconfig
@@ -0,0 +1,52 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+CONFIG_ARCH="risc-v"
+CONFIG_ARCH_BOARD="esp32c6-devkitc"
+CONFIG_ARCH_BOARD_COMMON=y
+CONFIG_ARCH_BOARD_ESP32C6_DEVKITC=y
+CONFIG_ARCH_CHIP="esp32c6"
+CONFIG_ARCH_CHIP_ESP32C6=y
+CONFIG_ARCH_CHIP_ESP32C6WROOM1=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_RISCV=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=15000
+CONFIG_BUILTIN=y
+CONFIG_DEV_ZERO=y
+CONFIG_ESPRESSIF_ESP32C6=y
+CONFIG_ESP_MCPWM=y
+CONFIG_ESP_MCPWM_MOTOR=y
+CONFIG_ESP_MCPWM_MOTOR_BDC=y
+CONFIG_ESP_MCPWM_MOTOR_CH0=y
+CONFIG_FS_PROCFS=y
+CONFIG_IDLETHREAD_STACKSIZE=2048
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LIBC_PERROR_STDOUT=y
+CONFIG_LIBC_STRERROR=y
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_NSH_STRERROR=y
+CONFIG_PREALLOC_TIMERS=0
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_BACKTRACE=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_START_DAY=29
+CONFIG_START_MONTH=11
+CONFIG_START_YEAR=2019
+CONFIG_SYSTEM_DUMPSTACK=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
diff --git a/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c 
b/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c
index 96aadb503b..c9384cc162 100644
--- a/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c
+++ b/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c
@@ -348,6 +348,14 @@ int esp_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+  ret = board_motor_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: board_motor_initialize failed: %d\n", ret);
+    }
+#endif
+
   /* If we got here then perhaps not all initialization was successful, but
    * at least enough succeeded to bring-up NSH with perhaps reduced
    * capabilities.
diff --git a/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h 
b/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h
index 3572dfe2ce..70cc0d2f88 100644
--- a/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h
+++ b/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h
@@ -43,7 +43,24 @@ extern "C"
  * Public Function Prototypes
  ****************************************************************************/
 
-#ifdef CONFIG_ESP_MCPWM_CAPTURE
+/****************************************************************************
+ * Name: board_motor_initialize
+ *
+ * Description:
+ *   Initialize MCPWM peripheral for motor control and register the motor
+ *   driver.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+int board_motor_initialize(void);
+#endif
 
 /****************************************************************************
  * Name: board_capture_initialize
@@ -59,9 +76,10 @@ extern "C"
  *
  ****************************************************************************/
 
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
 int board_capture_initialize(void);
+#endif
 
-#endif /* CONFIG_ESP_MCPWM_CAPTURE */
 #undef EXTERN
 #ifdef __cplusplus
 }
diff --git a/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c 
b/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c
index e5a080af07..3a24912107 100644
--- a/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c
+++ b/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c
@@ -29,7 +29,12 @@
 #include <debug.h>
 
 #include <nuttx/board.h>
+#ifdef CONFIG_MOTOR
+#include <nuttx/motor/motor.h>
+#endif
+#ifdef CONFIG_CAPTURE
 #include <nuttx/timers/capture.h>
+#endif
 
 #include <arch/board/board.h>
 
@@ -39,10 +44,61 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
+#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT
+#  define MCPWM_FAULT_GPIO CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT_GPIO
+#else
+#  define MCPWM_FAULT_GPIO 0
+#endif
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: board_motor_initialize
+ *
+ * Description:
+ *   Initialize MCPWM peripheral for motor control and register the motor
+ *   driver.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+int board_motor_initialize(void)
+{
+  int ret;
+  struct motor_lowerhalf_s *motor;
+
+#ifdef CONFIG_ESP_MCPWM_MOTOR_CH0
+  motor = esp_motor_bdc_initialize(0,
+                                   CONFIG_ESP_MCPWM_MOTOR_CH0_PWM_FREQ,
+                                   CONFIG_ESP_MCPWM_MOTOR_CH0_PWMA_GPIO,
+                                   CONFIG_ESP_MCPWM_MOTOR_CH0_PWMB_GPIO,
+                                   MCPWM_FAULT_GPIO);
+  if (motor == NULL)
+    {
+      syslog(LOG_ERR, "ERROR: Failed to start MCPWM BDC Motor: CH0\n");
+      return -ENODEV;
+    }
+
+  ret = motor_register("/dev/motor0", motor);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: motor_register failed: %d\n", ret);
+      return ret;
+    }
+#endif
+
+  return OK;
+}
+#endif
+
 /****************************************************************************
  * Name: board_capture_initialize
  *
@@ -57,6 +113,7 @@
  *
  ****************************************************************************/
 
+#ifdef CONFIG_ESP_MCPWM_CAPTURE
 int board_capture_initialize(void)
 {
   int ret;
@@ -112,3 +169,4 @@ int board_capture_initialize(void)
 
   return OK;
 }
+#endif
diff --git a/boards/risc-v/esp32h2/esp32h2-devkit/configs/motor/defconfig 
b/boards/risc-v/esp32h2/esp32h2-devkit/configs/motor/defconfig
new file mode 100644
index 0000000000..b83654b587
--- /dev/null
+++ b/boards/risc-v/esp32h2/esp32h2-devkit/configs/motor/defconfig
@@ -0,0 +1,51 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+CONFIG_ARCH="risc-v"
+CONFIG_ARCH_BOARD="esp32h2-devkit"
+CONFIG_ARCH_BOARD_COMMON=y
+CONFIG_ARCH_BOARD_ESP32H2_DEVKIT=y
+CONFIG_ARCH_CHIP="esp32h2"
+CONFIG_ARCH_CHIP_ESP32H2=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_RISCV=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_BOARDCTL_RESET=y
+CONFIG_BOARD_LOOPSPERMSEC=15000
+CONFIG_BUILTIN=y
+CONFIG_DEV_ZERO=y
+CONFIG_ESPRESSIF_ESP32H2=y
+CONFIG_ESP_MCPWM=y
+CONFIG_ESP_MCPWM_MOTOR=y
+CONFIG_ESP_MCPWM_MOTOR_BDC=y
+CONFIG_ESP_MCPWM_MOTOR_CH0=y
+CONFIG_FS_PROCFS=y
+CONFIG_IDLETHREAD_STACKSIZE=2048
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INTELHEX_BINARY=y
+CONFIG_LIBC_PERROR_STDOUT=y
+CONFIG_LIBC_STRERROR=y
+CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_NSH_STRERROR=y
+CONFIG_PREALLOC_TIMERS=0
+CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_BACKTRACE=y
+CONFIG_SCHED_WAITPID=y
+CONFIG_START_DAY=29
+CONFIG_START_MONTH=11
+CONFIG_START_YEAR=2019
+CONFIG_SYSTEM_DUMPSTACK=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_UART0_SERIAL_CONSOLE=y
diff --git a/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c 
b/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c
index 499a17b5b5..f6afd1f4d6 100644
--- a/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c
+++ b/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c
@@ -315,6 +315,14 @@ int esp_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_ESP_MCPWM_MOTOR
+  ret = board_motor_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: board_motor_initialize failed: %d\n", ret);
+    }
+#endif
+
   /* If we got here then perhaps not all initialization was successful, but
    * at least enough succeeded to bring-up NSH with perhaps reduced
    * capabilities.

Reply via email to