| /* |
| * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT espressif_esp32_mcpwm |
| |
| #include <hal/mcpwm_hal.h> |
| #include <hal/mcpwm_ll.h> |
| #include <driver/mcpwm.h> |
| |
| #include <soc.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <zephyr/drivers/pwm.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/drivers/clock_control.h> |
| #ifdef CONFIG_PWM_CAPTURE |
| #include <zephyr/drivers/interrupt_controller/intc_esp32.h> |
| #endif /* CONFIG_PWM_CAPTURE */ |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(mcpwm_esp32, CONFIG_PWM_LOG_LEVEL); |
| |
| #define SOC_MCPWM_BASE_CLK_HZ (160000000U) |
| #ifdef CONFIG_PWM_CAPTURE |
| #define SKIP_IRQ_NUM 4U |
| #define MCPWM_INTR_CAP0 BIT(0) |
| #define MCPWM_INTR_CAP1 BIT(1) |
| #define MCPWM_INTR_CAP2 BIT(2) |
| #define MCPWM_CHANNEL_NUM 8U |
| #define CAPTURE_CHANNEL_IDX 6U |
| #else |
| #define MCPWM_CHANNEL_NUM 6U |
| #endif /* CONFIG_PWM_CAPTURE */ |
| |
| struct mcpwm_esp32_data { |
| mcpwm_hal_context_t hal; |
| mcpwm_hal_init_config_t init_config; |
| struct k_sem cmd_sem; |
| }; |
| |
| #ifdef CONFIG_PWM_CAPTURE |
| struct capture_data { |
| uint32_t value; |
| mcpwm_capture_on_edge_t edge; |
| }; |
| |
| struct mcpwm_esp32_capture_config { |
| uint8_t capture_signal; |
| pwm_capture_callback_handler_t callback; |
| void *user_data; |
| uint32_t period; |
| uint32_t pulse; |
| uint32_t overflows; |
| uint8_t skip_irq; |
| bool capture_period; |
| bool capture_pulse; |
| bool continuous; |
| struct capture_data capture_data[SKIP_IRQ_NUM]; |
| }; |
| #endif /* CONFIG_PWM_CAPTURE */ |
| |
| struct mcpwm_esp32_channel_config { |
| uint8_t idx; |
| uint8_t timer_id; |
| uint8_t operator_id; |
| uint8_t generator_id; |
| uint32_t freq; |
| uint32_t duty; |
| uint8_t prescale; |
| bool inverted; |
| #ifdef CONFIG_PWM_CAPTURE |
| struct mcpwm_esp32_capture_config capture; |
| #endif /* CONFIG_PWM_CAPTURE */ |
| }; |
| |
| struct mcpwm_esp32_config { |
| const uint8_t index; |
| const struct pinctrl_dev_config *pincfg; |
| const struct device *clock_dev; |
| const clock_control_subsys_t clock_subsys; |
| uint8_t prescale; |
| uint8_t prescale_timer0; |
| uint8_t prescale_timer1; |
| uint8_t prescale_timer2; |
| struct mcpwm_esp32_channel_config channel_config[MCPWM_CHANNEL_NUM]; |
| #ifdef CONFIG_PWM_CAPTURE |
| int (*irq_config_func)(const struct device *dev); |
| #endif /* CONFIG_PWM_CAPTURE */ |
| }; |
| |
| static void mcpwm_esp32_duty_set(const struct device *dev, |
| struct mcpwm_esp32_channel_config *channel) |
| { |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| mcpwm_duty_type_t duty_type; |
| uint32_t set_duty; |
| |
| if (channel->inverted) { |
| duty_type = channel->duty == 0 ? |
| MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH : channel->duty == 100 ? |
| MCPWM_HAL_GENERATOR_MODE_FORCE_LOW : MCPWM_DUTY_MODE_1; |
| } else { |
| duty_type = channel->duty == 0 ? |
| MCPWM_HAL_GENERATOR_MODE_FORCE_LOW : channel->duty == 100 ? |
| MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH : MCPWM_DUTY_MODE_0; |
| } |
| |
| set_duty = mcpwm_ll_timer_get_peak(data->hal.dev, channel->timer_id, false) * |
| channel->duty / 100; |
| mcpwm_ll_operator_connect_timer(data->hal.dev, channel->operator_id, channel->timer_id); |
| mcpwm_ll_operator_set_compare_value(data->hal.dev, channel->operator_id, |
| channel->generator_id, set_duty); |
| mcpwm_ll_operator_enable_update_compare_on_tez(data->hal.dev, channel->operator_id, |
| channel->generator_id, true); |
| |
| if (duty_type == MCPWM_DUTY_MODE_0) { |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH); |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_KEEP); |
| mcpwm_ll_generator_set_action_on_compare_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_LOW); |
| } else if (duty_type == MCPWM_DUTY_MODE_1) { |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW); |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_ACTION_NO_CHANGE); |
| mcpwm_ll_generator_set_action_on_compare_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_HIGH); |
| } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_LOW) { |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_ACTION_FORCE_LOW); |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_ACTION_FORCE_LOW); |
| mcpwm_ll_generator_set_action_on_compare_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_LOW); |
| } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH) { |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_ACTION_FORCE_HIGH); |
| mcpwm_ll_generator_set_action_on_timer_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_ACTION_FORCE_HIGH); |
| mcpwm_ll_generator_set_action_on_compare_event( |
| data->hal.dev, channel->operator_id, channel->generator_id, |
| MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_HIGH); |
| } |
| } |
| |
| static int mcpwm_esp32_configure_pinctrl(const struct device *dev) |
| { |
| int ret; |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| |
| ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); |
| if (ret < 0) { |
| LOG_ERR("PWM pinctrl setup failed (%d)", ret); |
| return ret; |
| } |
| return 0; |
| } |
| |
| static int mcpwm_esp32_timer_set(const struct device *dev, |
| struct mcpwm_esp32_channel_config *channel) |
| { |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| |
| __ASSERT_NO_MSG(channel->freq > 0); |
| |
| mcpwm_ll_timer_set_clock_prescale(data->hal.dev, channel->timer_id, channel->prescale); |
| mcpwm_ll_timer_set_count_mode(data->hal.dev, channel->timer_id, MCPWM_TIMER_COUNT_MODE_UP); |
| mcpwm_ll_timer_update_period_at_once(data->hal.dev, channel->timer_id); |
| int real_group_prescale = mcpwm_ll_group_get_clock_prescale(data->hal.dev); |
| uint32_t real_timer_clk_hz = |
| SOC_MCPWM_BASE_CLK_HZ / real_group_prescale / |
| mcpwm_ll_timer_get_clock_prescale(data->hal.dev, channel->timer_id); |
| mcpwm_ll_timer_set_peak(data->hal.dev, channel->timer_id, real_timer_clk_hz / channel->freq, |
| false); |
| |
| return 0; |
| } |
| |
| static int mcpwm_esp32_get_cycles_per_sec(const struct device *dev, uint32_t channel_idx, |
| uint64_t *cycles) |
| { |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
| |
| if (!channel) { |
| LOG_ERR("Error getting channel %d", channel_idx); |
| return -EINVAL; |
| } |
| |
| #ifdef CONFIG_PWM_CAPTURE |
| if (channel->idx >= CAPTURE_CHANNEL_IDX) { |
| *cycles = (uint64_t)APB_CLK_FREQ; |
| return 0; |
| } |
| #endif /* CONFIG_PWM_CAPTURE */ |
| |
| *cycles = |
| (uint64_t)SOC_MCPWM_BASE_CLK_HZ / (config->prescale + 1) / (channel->prescale + 1); |
| |
| return 0; |
| } |
| |
| static int mcpwm_esp32_set_cycles(const struct device *dev, uint32_t channel_idx, |
| uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags) |
| { |
| int ret = 0; |
| uint64_t clk_freq; |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
| |
| if (!channel) { |
| LOG_ERR("Error getting channel %d", channel_idx); |
| return -EINVAL; |
| } |
| |
| /* Update PWM frequency according to period_cycles */ |
| mcpwm_esp32_get_cycles_per_sec(dev, channel_idx, &clk_freq); |
| |
| channel->freq = (uint32_t)(clk_freq / period_cycles); |
| if (!channel->freq) { |
| return -EINVAL; |
| } |
| |
| k_sem_take(&data->cmd_sem, K_FOREVER); |
| |
| ret = mcpwm_esp32_timer_set(dev, channel); |
| if (ret < 0) { |
| k_sem_give(&data->cmd_sem); |
| return ret; |
| } |
| |
| double duty_cycle = (double)pulse_cycles * 100 / (double)period_cycles; |
| |
| channel->duty = (uint32_t)duty_cycle; |
| |
| channel->inverted = (flags & PWM_POLARITY_INVERTED); |
| |
| mcpwm_esp32_duty_set(dev, channel); |
| |
| ret = mcpwm_esp32_configure_pinctrl(dev); |
| if (ret < 0) { |
| k_sem_give(&data->cmd_sem); |
| return ret; |
| } |
| |
| mcpwm_ll_timer_set_start_stop_command(data->hal.dev, channel->timer_id, |
| MCPWM_TIMER_START_NO_STOP); |
| |
| k_sem_give(&data->cmd_sem); |
| |
| return ret; |
| } |
| |
| #ifdef CONFIG_PWM_CAPTURE |
| static int mcpwm_esp32_configure_capture(const struct device *dev, uint32_t channel_idx, |
| pwm_flags_t flags, pwm_capture_callback_handler_t cb, |
| void *user_data) |
| { |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
| struct mcpwm_esp32_capture_config *capture = &channel->capture; |
| |
| if (!channel) { |
| LOG_ERR("Error getting channel %d", channel_idx); |
| return -EINVAL; |
| } |
| |
| if ((channel->idx < CAPTURE_CHANNEL_IDX) || (channel->idx > CAPTURE_CHANNEL_IDX + 2)) { |
| LOG_ERR("PWM capture only supported on channels 6, 7 and 8"); |
| return -EINVAL; |
| } |
| |
| if (data->hal.dev->cap_chn_cfg[capture->capture_signal].capn_en) { |
| LOG_ERR("PWM Capture already in progress"); |
| return -EBUSY; |
| } |
| |
| if (!(flags & PWM_CAPTURE_TYPE_MASK)) { |
| LOG_ERR("No PWM capture type specified"); |
| return -EINVAL; |
| } |
| |
| channel->inverted = (flags & PWM_POLARITY_INVERTED); |
| capture->capture_signal = channel->idx - CAPTURE_CHANNEL_IDX; |
| capture->callback = cb; |
| capture->user_data = user_data; |
| capture->capture_period = (flags & PWM_CAPTURE_TYPE_PERIOD); |
| capture->capture_pulse = (flags & PWM_CAPTURE_TYPE_PULSE); |
| capture->continuous = (flags & PWM_CAPTURE_MODE_CONTINUOUS); |
| |
| return 0; |
| } |
| |
| static int mcpwm_esp32_disable_capture(const struct device *dev, uint32_t channel_idx) |
| { |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
| struct mcpwm_esp32_capture_config *capture = &channel->capture; |
| |
| if (!channel) { |
| LOG_ERR("Error getting channel %d", channel_idx); |
| return -EINVAL; |
| } |
| |
| if ((channel->idx < CAPTURE_CHANNEL_IDX) || (channel->idx > CAPTURE_CHANNEL_IDX + 2)) { |
| LOG_ERR("PWM capture only supported on channels 6, 7 and 8"); |
| return -EINVAL; |
| } |
| |
| mcpwm_ll_capture_enable_channel(data->hal.dev, capture->capture_signal, false); |
| mcpwm_ll_intr_enable(data->hal.dev, MCPWM_LL_EVENT_CAPTURE(capture->capture_signal), false); |
| |
| return 0; |
| } |
| |
| static int mcpwm_esp32_enable_capture(const struct device *dev, uint32_t channel_idx) |
| { |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
| struct mcpwm_esp32_capture_config *capture = &channel->capture; |
| |
| if (!channel) { |
| LOG_ERR("Error getting channel %d", channel_idx); |
| return -EINVAL; |
| } |
| |
| if (!capture->callback) { |
| LOG_ERR("Capture not configured"); |
| return -EINVAL; |
| } |
| |
| if ((channel->idx < CAPTURE_CHANNEL_IDX) || (channel->idx > CAPTURE_CHANNEL_IDX + 2)) { |
| LOG_ERR("PWM capture only supported on channels 6, 7 and 8"); |
| return -EINVAL; |
| } |
| |
| if (data->hal.dev->cap_chn_cfg[capture->capture_signal].capn_en) { |
| LOG_ERR("PWM Capture already in progress"); |
| return -EBUSY; |
| } |
| |
| /** |
| * Capture prescale is different from other modules as it is applied to the input |
| * signal, not the timer source. It is disabled by default. |
| */ |
| mcpwm_capture_config_t cap_conf = { |
| .cap_edge = MCPWM_BOTH_EDGE, |
| .cap_prescale = 1, |
| }; |
| |
| mcpwm_hal_init(&data->hal, &data->init_config); |
| mcpwm_ll_group_set_clock_prescale(data->hal.dev, config->prescale); |
| mcpwm_ll_group_enable_shadow_mode(data->hal.dev); |
| mcpwm_ll_group_flush_shadow(data->hal.dev); |
| |
| mcpwm_ll_capture_enable_timer(data->hal.dev, true); |
| mcpwm_ll_capture_enable_channel(data->hal.dev, capture->capture_signal, true); |
| mcpwm_ll_capture_enable_negedge(data->hal.dev, capture->capture_signal, |
| cap_conf.cap_edge & MCPWM_NEG_EDGE); |
| mcpwm_ll_capture_enable_posedge(data->hal.dev, capture->capture_signal, |
| cap_conf.cap_edge & MCPWM_POS_EDGE); |
| mcpwm_ll_capture_set_prescale(data->hal.dev, capture->capture_signal, |
| cap_conf.cap_prescale); |
| |
| mcpwm_ll_intr_enable(data->hal.dev, MCPWM_LL_EVENT_CAPTURE(capture->capture_signal), true); |
| mcpwm_ll_intr_clear_capture_status(data->hal.dev, 1 << capture->capture_signal); |
| |
| capture->skip_irq = 0; |
| |
| return 0; |
| } |
| #endif /* CONFIG_PWM_CAPTURE */ |
| |
| static void channel_init(const struct device *dev) |
| { |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_channel_config *channel; |
| |
| for (uint8_t i = 0; i < MCPWM_CHANNEL_NUM; i++) { |
| channel = &config->channel_config[i]; |
| channel->idx = i; |
| channel->timer_id = i < 2 ? 0 : i < 4 ? 1 : 2; |
| channel->operator_id = i < 2 ? 0 : i < 4 ? 1 : 2; |
| channel->generator_id = i % 2 ? 1 : 0; |
| channel->prescale = i < 2 ? config->prescale_timer0 |
| : i < 4 ? config->prescale_timer1 |
| : config->prescale_timer2; |
| } |
| } |
| |
| int mcpwm_esp32_init(const struct device *dev) |
| { |
| int ret; |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| |
| if (!device_is_ready(config->clock_dev)) { |
| LOG_ERR("clock control device not ready"); |
| return -ENODEV; |
| } |
| |
| /* Enable peripheral */ |
| ret = clock_control_on(config->clock_dev, config->clock_subsys); |
| if (ret < 0) { |
| LOG_ERR("Could not initialize clock (%d)", ret); |
| return ret; |
| } |
| |
| channel_init(dev); |
| |
| mcpwm_hal_init(&data->hal, &data->init_config); |
| mcpwm_ll_group_set_clock_prescale(data->hal.dev, config->prescale); |
| mcpwm_ll_group_enable_shadow_mode(data->hal.dev); |
| mcpwm_ll_group_flush_shadow(data->hal.dev); |
| |
| #ifdef CONFIG_PWM_CAPTURE |
| ret = config->irq_config_func(dev); |
| |
| if (ret != 0) { |
| LOG_ERR("could not allocate interrupt (err %d)", ret); |
| } |
| #endif /* CONFIG_PWM_CAPTURE */ |
| return ret; |
| } |
| |
| #ifdef CONFIG_PWM_CAPTURE |
| static void IRAM_ATTR mcpwm_esp32_isr(const struct device *dev) |
| { |
| struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
| struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
| struct mcpwm_esp32_channel_config *channel; |
| struct mcpwm_esp32_capture_config *capture; |
| uint32_t mcpwm_intr_status; |
| |
| mcpwm_intr_status = mcpwm_ll_intr_get_capture_status(data->hal.dev); |
| |
| mcpwm_ll_intr_clear_capture_status(data->hal.dev, mcpwm_intr_status); |
| |
| if (mcpwm_intr_status & MCPWM_INTR_CAP0) { |
| channel = &config->channel_config[CAPTURE_CHANNEL_IDX]; |
| } else if (mcpwm_intr_status & MCPWM_INTR_CAP1) { |
| channel = &config->channel_config[CAPTURE_CHANNEL_IDX + 1]; |
| } else if (mcpwm_intr_status & MCPWM_INTR_CAP2) { |
| channel = &config->channel_config[CAPTURE_CHANNEL_IDX + 2]; |
| } else { |
| return; |
| } |
| |
| if (!channel) { |
| return; |
| } |
| |
| capture = &channel->capture; |
| |
| /* We need to wait at least 4 (2 positive edges and 2 negative edges) interrupts to |
| * calculate the period |
| */ |
| if (capture->skip_irq < SKIP_IRQ_NUM) { |
| capture->capture_data[capture->skip_irq].value = |
| mcpwm_ll_capture_get_value(data->hal.dev, capture->capture_signal); |
| capture->capture_data[capture->skip_irq].edge = |
| mcpwm_ll_capture_get_edge(data->hal.dev, capture->capture_signal) == |
| MCPWM_CAP_EDGE_NEG |
| ? MCPWM_NEG_EDGE |
| : MCPWM_POS_EDGE; |
| capture->skip_irq++; |
| |
| } else { |
| /** |
| * The capture timer is a 32-bit counter incrementing continuously, once enabled. |
| * On the input it has an APB clock running typically at 80 MHz |
| */ |
| capture->period = channel->inverted ? |
| capture->capture_data[0].edge == MCPWM_NEG_EDGE |
| ? (capture->capture_data[2].value - capture->capture_data[0].value) |
| : (capture->capture_data[3].value - capture->capture_data[1].value) |
| : capture->capture_data[0].edge == MCPWM_POS_EDGE |
| ? (capture->capture_data[2].value - capture->capture_data[0].value) |
| : (capture->capture_data[3].value - capture->capture_data[1].value); |
| |
| capture->pulse = channel->inverted ? |
| capture->capture_data[0].edge == MCPWM_NEG_EDGE |
| ? (capture->capture_data[1].value - capture->capture_data[0].value) |
| : (capture->capture_data[2].value - capture->capture_data[1].value) |
| : capture->capture_data[0].edge == MCPWM_POS_EDGE |
| ? (capture->capture_data[1].value - capture->capture_data[0].value) |
| : (capture->capture_data[2].value - capture->capture_data[1].value); |
| |
| capture->skip_irq = 0; |
| if (!capture->continuous) { |
| mcpwm_esp32_disable_capture(dev, channel->idx); |
| } |
| |
| if (capture->callback) { |
| capture->callback(dev, capture->capture_signal + CAPTURE_CHANNEL_IDX, |
| capture->capture_period ? capture->period : 0u, |
| capture->capture_pulse ? capture->pulse : 0u, 0u, |
| capture->user_data); |
| } |
| } |
| } |
| #endif /* CONFIG_PWM_CAPTURE */ |
| |
| static const struct pwm_driver_api mcpwm_esp32_api = { |
| .set_cycles = mcpwm_esp32_set_cycles, |
| .get_cycles_per_sec = mcpwm_esp32_get_cycles_per_sec, |
| #ifdef CONFIG_PWM_CAPTURE |
| .configure_capture = mcpwm_esp32_configure_capture, |
| .enable_capture = mcpwm_esp32_enable_capture, |
| .disable_capture = mcpwm_esp32_disable_capture, |
| #endif /* CONFIG_PWM_CAPTURE */ |
| }; |
| |
| #ifdef CONFIG_PWM_CAPTURE |
| #define IRQ_CONFIG_FUNC(idx) \ |
| static int mcpwm_esp32_irq_config_func_##idx(const struct device *dev) \ |
| { \ |
| int ret; \ |
| ret = esp_intr_alloc(DT_INST_IRQ_BY_IDX(idx, 0, irq), \ |
| ESP_PRIO_TO_FLAGS(DT_INST_IRQ_BY_IDX(idx, 0, priority)) | \ |
| ESP_INT_FLAGS_CHECK(DT_INST_IRQ_BY_IDX(idx, 0, flags)) | \ |
| ESP_INTR_FLAG_IRAM, \ |
| (intr_handler_t)mcpwm_esp32_isr, (void *)dev, NULL); \ |
| return ret; \ |
| } |
| #define CAPTURE_INIT(idx) .irq_config_func = mcpwm_esp32_irq_config_func_##idx |
| #else |
| #define IRQ_CONFIG_FUNC(idx) |
| #define CAPTURE_INIT(idx) |
| #endif /* CONFIG_PWM_CAPTURE */ |
| |
| #define ESP32_MCPWM_INIT(idx) \ |
| PINCTRL_DT_INST_DEFINE(idx); \ |
| IRQ_CONFIG_FUNC(idx); \ |
| static struct mcpwm_esp32_data mcpwm_esp32_data_##idx = { \ |
| .hal = \ |
| { \ |
| .dev = (mcpwm_dev_t *)DT_INST_REG_ADDR(idx), \ |
| }, \ |
| .init_config = \ |
| { \ |
| .group_id = idx, \ |
| }, \ |
| .cmd_sem = Z_SEM_INITIALIZER(mcpwm_esp32_data_##idx.cmd_sem, 1, 1), \ |
| }; \ |
| \ |
| static struct mcpwm_esp32_config mcpwm_esp32_config_##idx = { \ |
| .index = idx, \ |
| .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ |
| .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \ |
| .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(idx, offset), \ |
| .prescale = DT_INST_PROP(idx, prescale), \ |
| .prescale_timer0 = DT_INST_PROP_OR(idx, prescale_timer0, 0), \ |
| .prescale_timer1 = DT_INST_PROP_OR(idx, prescale_timer1, 0), \ |
| .prescale_timer2 = DT_INST_PROP_OR(idx, prescale_timer2, 0), \ |
| CAPTURE_INIT(idx)}; \ |
| \ |
| DEVICE_DT_INST_DEFINE(idx, &mcpwm_esp32_init, NULL, &mcpwm_esp32_data_##idx, \ |
| &mcpwm_esp32_config_##idx, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ |
| &mcpwm_esp32_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(ESP32_MCPWM_INIT) |