From 447029d7c3efba99894472ec2d7c81d84620ee8d Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 6 Mar 2019 11:33:22 +0100 Subject: [PATCH] drivers: pwm: Add NXP MCUX PWM driver This driver handles eFlexPWM submodules via MCUX hal. Each submodule has two channels (A/B). Signed-off-by: Loic Poulain --- drivers/pwm/CMakeLists.txt | 1 + drivers/pwm/Kconfig | 2 + drivers/pwm/Kconfig.mcux | 113 ++++++++++ drivers/pwm/pwm_mcux.c | 228 ++++++++++++++++++++ ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt | 1 + 5 files changed, 345 insertions(+) create mode 100644 drivers/pwm/Kconfig.mcux create mode 100644 drivers/pwm/pwm_mcux.c diff --git a/drivers/pwm/CMakeLists.txt b/drivers/pwm/CMakeLists.txt index c015b986406..e290a0d5c14 100644 --- a/drivers/pwm/CMakeLists.txt +++ b/drivers/pwm/CMakeLists.txt @@ -13,5 +13,6 @@ zephyr_library_sources_ifdef(CONFIG_PWM_MCUX_FTM pwm_mcux_ftm.c) zephyr_library_sources_ifdef(CONFIG_PWM_IMX pwm_imx.c) zephyr_library_sources_ifdef(CONFIG_PWM_LED_ESP32 pwm_led_esp32.c) zephyr_library_sources_ifdef(CONFIG_PWM_SAM pwm_sam.c) +zephyr_library_sources_ifdef(CONFIG_PWM_MCUX pwm_mcux.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE pwm_handlers.c) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 30f6af6d57c..bfd9e794733 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -54,4 +54,6 @@ source "drivers/pwm/Kconfig.esp32" source "drivers/pwm/Kconfig.sam" +source "drivers/pwm/Kconfig.mcux" + endif # PWM diff --git a/drivers/pwm/Kconfig.mcux b/drivers/pwm/Kconfig.mcux new file mode 100644 index 00000000000..1d967ea2a80 --- /dev/null +++ b/drivers/pwm/Kconfig.mcux @@ -0,0 +1,113 @@ +# Kconfig.stm32 - STM32 PWM configuration options +# +# +# Copyright (c) 2019 Linaro Limited. +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig PWM_MCUX + bool "MCUX PWM driver" + depends on HAS_MCUX_PWM + help + Enable mcux pwm driver. + +if PWM_MCUX + +config FLEXPWM1_PWM0 + bool "NXP MCUX FLEXPWM1_PWM0 Output" + help + Enable output for FLEXPWM1_PWM0 in the driver. Say y here + if you want to use FLEXPWM1_PWM0 output. + +config FLEXPWM1_PWM1 + bool "NXP MCUX FLEXPWM1_PWM1 Output" + help + Enable output for FLEXPWM1_PWM1 in the driver. Say y here + if you want to use FLEXPWM1_PWM1 output. + +config FLEXPWM1_PWM2 + bool "NXP MCUX FLEXPWM1_PWM2 Output" + help + Enable output for FLEXPWM1_PWM2 in the driver. Say y here + if you want to use FLEXPWM1_PWM2 output. + +config FLEXPWM1_PWM3 + bool "NXP MCUX FLEXPWM1_PWM3 Output" + help + Enable output for FLEXPWM1_PWM3 in the driver. Say y here + if you want to use FLEXPWM1_PWM3 output. + +config FLEXPWM2_PWM0 + bool "NXP MCUX FLEXPWM2_PWM0 Output" + help + Enable output for FLEXPWM2_PWM0 in the driver. Say y here + if you want to use FLEXPWM2_PWM0 output. + +config FLEXPWM2_PWM1 + bool "NXP MCUX FLEXPWM2_PWM1 Output" + help + Enable output for FLEXPWM2_PWM1 in the driver. Say y here + if you want to use FLEXPWM2_PWM1 output. + +config FLEXPWM2_PWM2 + bool "NXP MCUX FLEXPWM2_PWM2 Output" + help + Enable output for FLEXPWM2_PWM2 in the driver. Say y here + if you want to use FLEXPWM2_PWM2 output. + +config FLEXPWM2_PWM3 + bool "NXP MCUX FLEXPWM2_PWM3 Output" + help + Enable output for FLEXPWM2_PWM3 in the driver. Say y here + if you want to use FLEXPWM2_PWM3 output. + +config FLEXPWM3_PWM0 + bool "NXP MCUX FLEXPWM3_PWM0 Output" + help + Enable output for FLEXPWM3_PWM0 in the driver. Say y here + if you want to use FLEXPWM3_PWM0 output. + +config FLEXPWM3_PWM1 + bool "NXP MCUX FLEXPWM3_PWM1 Output" + help + Enable output for FLEXPWM3_PWM1 in the driver. Say y here + if you want to use FLEXPWM3_PWM1 output. + +config FLEXPWM3_PWM2 + bool "NXP MCUX FLEXPWM3_PWM2 Output" + help + Enable output for FLEXPWM3_PWM2 in the driver. Say y here + if you want to use FLEXPWM3_PWM2 output. + +config FLEXPWM3_PWM3 + bool "NXP MCUX FLEXPWM3_PWM3 Output" + help + Enable output for FLEXPWM3_PWM3 in the driver. Say y here + if you want to use FLEXPWM3_PWM3 output. + +config FLEXPWM4_PWM0 + bool "NXP MCUX FLEXPWM4_PWM0 Output" + help + Enable output for FLEXPWM4_PWM0 in the driver. Say y here + if you want to use FLEXPWM4_PWM0 output. + +config FLEXPWM4_PWM1 + bool "NXP MCUX FLEXPWM4_PWM1 Output" + help + Enable output for FLEXPWM4_PWM1 in the driver. Say y here + if you want to use FLEXPWM4_PWM1 output. + +config FLEXPWM4_PWM2 + bool "NXP MCUX FLEXPWM4_PWM2 Output" + help + Enable output for FLEXPWM4_PWM2 in the driver. Say y here + if you want to use FLEXPWM4_PWM2 output. + +config FLEXPWM4_PWM3 + bool "NXP MCUX FLEXPWM4_PWM3 Output" + help + Enable output for FLEXPWM4_PWM3 in the driver. Say y here + if you want to use FLEXPWM4_PWM3 output. + +endif # PWM_MCUX diff --git a/drivers/pwm/pwm_mcux.c b/drivers/pwm/pwm_mcux.c new file mode 100644 index 00000000000..9b5a0e50fde --- /dev/null +++ b/drivers/pwm/pwm_mcux.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2019, Linaro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_PWM_LOG_LEVEL +#include +LOG_MODULE_REGISTER(pwm_mcux); + +#define CHANNEL_COUNT 2 + +struct pwm_mcux_config { + PWM_Type *base; + uint8_t index; + clock_name_t clock_source; + pwm_clock_prescale_t prescale; + pwm_mode_t mode; +}; + +struct pwm_mcux_data { + uint32_t period_cycles[CHANNEL_COUNT]; + pwm_signal_param_t channel[CHANNEL_COUNT]; +}; + +static int mcux_pwm_pin_set(struct device *dev, u32_t pwm, + u32_t period_cycles, u32_t pulse_cycles) +{ + const struct pwm_mcux_config *config = dev->config->config_info; + struct pwm_mcux_data *data = dev->driver_data; + u8_t duty_cycle; + + if (pwm >= CHANNEL_COUNT) { + LOG_ERR("Invalid channel"); + return -EINVAL; + } + + if ((period_cycles == 0) || (pulse_cycles > period_cycles)) { + LOG_ERR("Invalid combination: period_cycles=%u, " + "pulse_cycles=%u", period_cycles, pulse_cycles); + return -EINVAL; + } + + if (period_cycles > UINT16_MAX) { + /* 16-bit resolution */ + LOG_ERR("Too long period (%u), adjust pwm prescaler!", + period_cycles); + /* TODO: dynamically adjust prescaler */ + return -EINVAL; + } + + duty_cycle = 100 * pulse_cycles / period_cycles; + + /* FIXME: Force re-setup even for duty-cycle update */ + if (period_cycles != data->period_cycles[pwm]) { + uint32_t clock_freq; + uint32_t pwm_freq; + status_t status; + + data->period_cycles[pwm] = period_cycles; + + LOG_DBG("SETUP dutycycle to %u\n", duty_cycle); + + clock_freq = CLOCK_GetFreq(config->clock_source); + pwm_freq = (clock_freq >> config->prescale) / period_cycles; + + if (pwm_freq == 0) { + LOG_ERR("Could not set up pwm_freq=%d", pwm_freq); + return -EINVAL; + } + + PWM_StopTimer(config->base, 1U << config->index); + + data->channel[pwm].dutyCyclePercent = duty_cycle; + + status = PWM_SetupPwm(config->base, config->index, + &data->channel[0], CHANNEL_COUNT, + config->mode, pwm_freq, clock_freq); + if (status != kStatus_Success) { + LOG_ERR("Could not set up pwm"); + return -ENOTSUP; + } + + PWM_SetPwmLdok(config->base, 1U << config->index, true); + + PWM_StartTimer(config->base, 1U << config->index); + } else { + PWM_UpdatePwmDutycycle(config->base, config->index, + (pwm == 0) ? kPWM_PwmA : kPWM_PwmB, + config->mode, duty_cycle); + PWM_SetPwmLdok(config->base, 1U << config->index, true); + } + + return 0; +} + +static int mcux_pwm_get_cycles_per_sec(struct device *dev, u32_t pwm, + u64_t *cycles) +{ + const struct pwm_mcux_config *config = dev->config->config_info; + + *cycles = CLOCK_GetFreq(config->clock_source) >> config->prescale; + + return 0; +} + +static int pwm_mcux_init(struct device *dev) +{ + const struct pwm_mcux_config *config = dev->config->config_info; + struct pwm_mcux_data *data = dev->driver_data; + pwm_config_t pwm_config; + status_t status; + + PWM_GetDefaultConfig(&pwm_config); + pwm_config.prescale = config->prescale; + pwm_config.reloadLogic = kPWM_ReloadPwmFullCycle; + + status = PWM_Init(config->base, config->index, &pwm_config); + if (status != kStatus_Success) { + LOG_ERR("Unable to init PWM"); + return -EIO; + } + + /* Disable fault sources */ + ((PWM_Type *)config->base)->SM[config->index].DISMAP[0] = 0x0000; + ((PWM_Type *)config->base)->SM[config->index].DISMAP[1] = 0x0000; + + data->channel[0].pwmChannel = kPWM_PwmA; + data->channel[0].level = kPWM_HighTrue; + data->channel[1].pwmChannel = kPWM_PwmB; + data->channel[1].level = kPWM_HighTrue; + + return 0; +} + +static const struct pwm_driver_api pwm_mcux_driver_api = { + .pin_set = mcux_pwm_pin_set, + .get_cycles_per_sec = mcux_pwm_get_cycles_per_sec, +}; + +#define PWM_DEVICE_INIT_MCUX(n) \ + static struct pwm_mcux_data pwm_mcux_data_ ## n; \ + \ + static const struct pwm_mcux_config pwm_mcux_config_ ## n = { \ + .base = (void *)DT_PWM_MCUX_ ## n ## _BASE_ADDRESS, \ + .index = DT_PWM_MCUX_ ## n ## _INDEX, \ + .mode = kPWM_EdgeAligned, \ + .prescale = kPWM_Prescale_Divide_128, \ + .clock_source = kCLOCK_IpgClk, \ + }; \ + \ + DEVICE_AND_API_INIT(pwm_mcux_ ## n, \ + DT_PWM_MCUX_ ## n ## _NAME, \ + pwm_mcux_init, \ + &pwm_mcux_data_ ## n, \ + &pwm_mcux_config_ ## n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,\ + &pwm_mcux_driver_api); + +#ifdef CONFIG_FLEXPWM1_PWM0 +PWM_DEVICE_INIT_MCUX(0) +#endif + +#ifdef CONFIG_FLEXPWM1_PWM1 +PWM_DEVICE_INIT_MCUX(1) +#endif + +#ifdef CONFIG_FLEXPWM1_PWM2 +PWM_DEVICE_INIT_MCUX(2) +#endif + +#ifdef CONFIG_FLEXPWM1_PWM3 +PWM_DEVICE_INIT_MCUX(3) +#endif + +#ifdef CONFIG_FLEXPWM2_PWM0 +PWM_DEVICE_INIT_MCUX(4) +#endif + +#ifdef CONFIG_FLEXPWM2_PWM1 +PWM_DEVICE_INIT_MCUX(5) +#endif + +#ifdef CONFIG_FLEXPWM2_PWM2 +PWM_DEVICE_INIT_MCUX(6) +#endif + +#ifdef CONFIG_FLEXPWM2_PWM3 +PWM_DEVICE_INIT_MCUX(7) +#endif + +#ifdef CONFIG_FLEXPWM3_PWM0 +PWM_DEVICE_INIT_MCUX(8) +#endif + +#ifdef CONFIG_FLEXPWM3_PWM1 +PWM_DEVICE_INIT_MCUX(9) +#endif + +#ifdef CONFIG_FLEXPWM3_PWM2 +PWM_DEVICE_INIT_MCUX(10) +#endif + +#ifdef CONFIG_FLEXPWM3_PWM3 +PWM_DEVICE_INIT_MCUX(11) +#endif + +#ifdef CONFIG_FLEXPWM4_PWM0 +PWM_DEVICE_INIT_MCUX(12) +#endif + +#ifdef CONFIG_FLEXPWM4_PWM1 +PWM_DEVICE_INIT_MCUX(13) +#endif + +#ifdef CONFIG_FLEXPWM4_PWM2 +PWM_DEVICE_INIT_MCUX(14) +#endif + +#ifdef CONFIG_FLEXPWM4_PWM3 +PWM_DEVICE_INIT_MCUX(15) +#endif diff --git a/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt b/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt index dffe01a01ff..3d24187f0ee 100644 --- a/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt +++ b/ext/hal/nxp/mcux/drivers/imx/CMakeLists.txt @@ -23,3 +23,4 @@ zephyr_sources_ifdef(CONFIG_UART_MCUX_LPUART fsl_lpuart.c) zephyr_sources_ifdef(CONFIG_COUNTER_MCUX_GPT fsl_gpt.c) zephyr_sources_ifdef(CONFIG_ETH_MCUX fsl_enet.c) zephyr_sources_ifdef(CONFIG_ENTROPY_MCUX_TRNG fsl_trng.c) +zephyr_sources_ifdef(CONFIG_PWM_MCUX fsl_pwm.c)