This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit addfe182aeb70b57dd48968cac2ad55a64249ac2 Author: Michal Lenc <[email protected]> AuthorDate: Sat Jun 26 11:49:38 2021 +0200 arch/arm/src/imxrt: added support for FlexPWM driver Signed-off-by: Michal Lenc <[email protected]> --- arch/arm/src/imxrt/Kconfig | 245 +++++++ arch/arm/src/imxrt/Make.defs | 4 + arch/arm/src/imxrt/hardware/imxrt_iomuxc.h | 3 + arch/arm/src/imxrt/imxrt_flexpwm.c | 1061 ++++++++++++++++++++++++++++ arch/arm/src/imxrt/imxrt_flexpwm.h | 78 ++ 5 files changed, 1391 insertions(+) diff --git a/arch/arm/src/imxrt/Kconfig b/arch/arm/src/imxrt/Kconfig index 4cff2b4..e1165ea 100644 --- a/arch/arm/src/imxrt/Kconfig +++ b/arch/arm/src/imxrt/Kconfig @@ -170,6 +170,11 @@ config IMXRT_FLEXCAN default n select ARCH_HAVE_NETDEV_STATISTICS +config IMXRT_FLEXPWM + bool + default n + select ARCH_HAVE_PWM_MULTICHAN + config IMXRT_LPI2C bool default n @@ -547,6 +552,246 @@ config FLEXCAN3_DATA_SAMPLEP endmenu # IMXRT_FLEXCAN3 +menu "FLEXPWM Peripherals" + +config IMXRT_FLEXPWM1 + bool "FLEXPWM1" + default n + select IMXRT_FLEXPWM + +config IMXRT_FLEXPWM2 + bool "FLEXPWM2" + default n + select IMXRT_FLEXPWM + +if ARCH_FAMILY_IMXRT105x || ARCH_FAMILY_IMXRT106x + +config IMXRT_FLEXPWM3 + bool "FLEXPWM3" + default n + select IMXRT_FLEXPWM + +config IMXRT_FLEXPWM4 + bool "FLEXPWM4" + default n + select IMXRT_FLEXPWM + +endif # ARCH_FAMILY_IMXRT105x || ARCH_FAMILY_IMXRT106x + +endmenu # FLEXPWM Peripherals + +menu "FLEXPWM1 Configuration" + depends on IMXRT_FLEXPWM1 + +config IMXRT_FLEXPWM1_MOD1 + bool "FLEXPWM1 Module 1" + default n + +if IMXRT_FLEXPWM1_MOD1 + +config IMXRT_FLEXPWM1_MOD1_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM1_MOD2 + bool "FLEXPWM1 Module 2" + default n + +if IMXRT_FLEXPWM1_MOD2 + +config IMXRT_FLEXPWM1_MOD2_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM1_MOD3 + bool "FLEXPWM1 Module 3" + default n + +if IMXRT_FLEXPWM1_MOD3 + +config IMXRT_FLEXPWM1_MOD3_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM1_MOD4 + bool "FLEXPWM1 Module 4" + default n + +if IMXRT_FLEXPWM1_MOD4 + +config IMXRT_FLEXPWM1_MOD4_COMP + bool "Use complementary output" + default n + +endif + +endmenu # IMXRT_FLEXPWM1 + +menu "FLEXPWM2 Configuration" + depends on IMXRT_FLEXPWM2 + +config IMXRT_FLEXPWM2_MOD1 + bool "FLEXPWM2 Module 1" + default n + +if IMXRT_FLEXPWM2_MOD1 + +config IMXRT_FLEXPWM2_MOD1_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM2_MOD2 + bool "FLEXPWM2 Module 2" + default n + +if IMXRT_FLEXPWM2_MOD2 + +config IMXRT_FLEXPWM2_MOD2_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM2_MOD3 + bool "FLEXPWM2 Module 3" + default n + +if IMXRT_FLEXPWM2_MOD3 + +config IMXRT_FLEXPWM2_MOD3_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM2_MOD4 + bool "FLEXPWM2 Module 4" + default n + +if IMXRT_FLEXPWM2_MOD4 + +config IMXRT_FLEXPWM2_MOD4_COMP + bool "Use complementary output" + default n + +endif + +endmenu # IMXRT_FLEXPWM2 + +menu "FLEXPWM3 Configuration" + depends on IMXRT_FLEXPWM3 + +config IMXRT_FLEXPWM3_MOD1 + bool "FLEXPWM3 Module 1" + default n + +if IMXRT_FLEXPWM3_MOD1 + +config IMXRT_FLEXPWM3_MOD1_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM3_MOD2 + bool "FLEXPWM3 Module 2" + default n + +if IMXRT_FLEXPWM3_MOD2 + +config IMXRT_FLEXPWM3_MOD2_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM3_MOD3 + bool "FLEXPWM3 Module 3" + default n + +if IMXRT_FLEXPWM3_MOD3 + +config IMXRT_FLEXPWM3_MOD3_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM3_MOD4 + bool "FLEXPWM3 Module 4" + default n + +if IMXRT_FLEXPWM3_MOD4 + +config IMXRT_FLEXPWM3_MOD4_COMP + bool "Use complementary output" + default n + +endif + +endmenu # IMXRT_FLEXPWM3 + +menu "FLEXPWM4 Configuration" + depends on IMXRT_FLEXPWM4 + +config IMXRT_FLEXPWM4_MOD1 + bool "FLEXPWM4 Module 1" + default n + +if IMXRT_FLEXPWM4_MOD1 + +config IMXRT_FLEXPWM4_MOD1_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM4_MOD2 + bool "FLEXPWM4 Module 2" + default n + +if IMXRT_FLEXPWM4_MOD2 + +config IMXRT_FLEXPWM4_MOD2_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM4_MOD3 + bool "FLEXPWM4 Module 3" + default n + +if IMXRT_FLEXPWM4_MOD3 + +config IMXRT_FLEXPWM4_MOD3_COMP + bool "Use complementary output" + default n + +endif + +config IMXRT_FLEXPWM4_MOD4 + bool "FLEXPWM4 Module 4" + default n + +if IMXRT_FLEXPWM4_MOD4 + +config IMXRT_FLEXPWM4_MOD4_COMP + bool "Use complementary output" + default n + +endif + +endmenu # IMXRT_FLEXPWM4 + menu "LPI2C Peripherals" menuconfig IMXRT_LPI2C1 diff --git a/arch/arm/src/imxrt/Make.defs b/arch/arm/src/imxrt/Make.defs index ba9195a..ae90261 100644 --- a/arch/arm/src/imxrt/Make.defs +++ b/arch/arm/src/imxrt/Make.defs @@ -116,6 +116,10 @@ ifeq ($(CONFIG_IMXRT_FLEXCAN),y) CHIP_CSRCS += imxrt_flexcan.c endif +ifeq ($(CONFIG_IMXRT_FLEXPWM),y) +CHIP_CSRCS += imxrt_flexpwm.c +endif + ifeq ($(CONFIG_IMXRT_SNVS_LPSRTC),y) CHIP_CSRCS += imxrt_lpsrtc.c CHIP_CSRCS += imxrt_hprtc.c diff --git a/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h b/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h index 2987af1..3f06ce7 100644 --- a/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h +++ b/arch/arm/src/imxrt/hardware/imxrt_iomuxc.h @@ -154,6 +154,9 @@ #define IOMUX_CAN_DEFAULT (IOMUX_SLEW_SLOW | \ IOMUX_DRIVE_50OHM | IOMUX_SPEED_LOW ) +#define IOMUX_PWM_DEFAULT (IOMUX_SLEW_FAST | \ + IOMUX_DRIVE_50OHM | IOMUX_SPEED_LOW ) + #define IOMUX_USDHC1_DATAX_DEFAULT (IOMUX_SLEW_FAST | IOMUX_DRIVE_130OHM | \ IOMUX_PULL_UP_47K | IOMUX_SCHMITT_TRIGGER) #define IOMUX_USDHC1_CMD_DEFAULT (IOMUX_SLEW_FAST | IOMUX_DRIVE_130OHM | \ diff --git a/arch/arm/src/imxrt/imxrt_flexpwm.c b/arch/arm/src/imxrt/imxrt_flexpwm.c new file mode 100644 index 0000000..5f4bf25 --- /dev/null +++ b/arch/arm/src/imxrt/imxrt_flexpwm.c @@ -0,0 +1,1061 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_flexpwm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <inttypes.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#include <debug.h> +#include <errno.h> + +#include <nuttx/timers/pwm.h> + +#include "arm_arch.h" +#include "chip.h" +#include "imxrt_config.h" +#include "imxrt_flexpwm.h" +#include "imxrt_periphclks.h" +#include "hardware/imxrt_flexpwm.h" +#include "hardware/imxrt_pinmux.h" +#include "hardware/imxrt_ccm.h" + +#include <arch/board/board.h> + +#include <sys/time.h> + +#ifdef CONFIG_IMXRT_FLEXPWM + +#ifdef CONFIG_PWM_NCHANNELS +# define PWM_NCHANNELS CONFIG_PWM_NCHANNELS +#else +# define PWM_NCHANNELS 1 +#endif + +#define MODULE_OFFSET 0x60 +#define CLK_FREQ 132000000 +#define PWM_RES 65535 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imxrt_flexpwm_out_s +{ + bool used; + uint32_t pin; /* Output pin */ +}; + +struct imxrt_flexpwm_module_s +{ + uint8_t module; /* Number of PWM module */ + bool used; /* True if the module is used */ + struct imxrt_flexpwm_out_s out_a; /* PWM output */ + struct imxrt_flexpwm_out_s out_b; /* PWM output */ + bool complementary; /* True if outputs are complementary */ + uint32_t irq; /* Combined interrupt */ +}; + +struct imxrt_flexpwm_s +{ + const struct pwm_ops_s *ops; /* PWM operations */ + FAR struct imxrt_flexpwm_module_s *modules; + uint8_t modules_num; /* Number of modules */ + uint32_t frequency; /* PWM frequency */ + uint32_t base; /* Base addres of peripheral register */ +}; + +/* PWM driver methods */ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev); +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info); +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/* Helper functions */ + +static int pwm_set_output(FAR struct pwm_lowerhalf_s *dev, uint8_t channel, + ub16_t duty); +static int pwm_change_freq(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, uint8_t channel); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl, +}; + +#ifdef CONFIG_IMXRT_FLEXPWM1 + +static struct imxrt_flexpwm_module_s g_pwm1_modules[] = +{ + /* FlexPWM1 has 4 submodules with 2 outputs for each */ + +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD1 + { + .module = 1, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD1_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD1_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD1_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD2 + { + .module = 2, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD2_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD2_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD2_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD3 + { + .module = 3, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD3_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD3_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD3_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD4 + { + .module = 4, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD4_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM1_MOD4_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM1_MOD4_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +}; + +static struct imxrt_flexpwm_s g_pwm1 = +{ + .ops = &g_pwmops, + .modules = g_pwm1_modules, + .modules_num = 4, + .frequency = 0, + .base = IMXRT_FLEXPWM1_BASE, +}; +#endif /* CONFIG_IMXRT_FLEXPWM1 */ + +#ifdef CONFIG_IMXRT_FLEXPWM2 + +static struct imxrt_flexpwm_module_s g_pwm2_modules[] = +{ + /* FlexPWM2 has 4 submodules with 2 outputs for each */ + +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD1 + { + .module = 1, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD1_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD1_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD1_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD2 + { + .module = 2, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD2_A, + }, +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD2_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD2_B + } + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD3 + { + .module = 3, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD3_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD3_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD3_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD4 + { + .module = 4, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD4_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM2_MOD4_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM2_MOD4_B + }, + .complementary = true +#else + .complementary = false +#endif + } +#endif +}; + +static struct imxrt_flexpwm_s g_pwm2 = +{ + .ops = &g_pwmops, + .modules = g_pwm2_modules, + .modules_num = 4, + .frequency = 0, + .base = IMXRT_FLEXPWM2_BASE, +}; +#endif /* CONFIG_IMXRT_FLEXPWM2 */ + +#ifdef CONFIG_IMXRT_FLEXPWM3 + +static struct imxrt_flexpwm_module_s g_pwm3_modules[] = +{ + /* FlexPWM3 has 4 submodules with 2 outputs for each */ + +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD1 + { + .module = 1, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD1_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD1_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD1_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD2 + { + .module = 2, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD2_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD2_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD2_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD3 + { + .module = 3, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD3_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD3_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD3_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD4 + { + .module = 4, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD4_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM3_MOD4_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM3_MOD4_B + }, + .complementary = true +#else + .complementary = false +#endif + }, +#endif +}; + +static struct imxrt_flexpwm_s g_pwm3 = +{ + .ops = &g_pwmops, + .modules = g_pwm3_modules, + .modules_num = 4, + .frequency = 0, + .base = IMXRT_FLEXPWM3_BASE, +}; +#endif /* CONFIG_IMXRT_FLEXPWM3 */ + +#ifdef CONFIG_IMXRT_FLEXPWM4 + +static struct imxrt_flexpwm_module_s g_pwm4_modules[] = +{ + /* FlexPWM4 has 4 submodules with 2 outputs for each */ + +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD1 + { + .module = 1, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD1_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD1_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD1_B + } + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD2 + { + .module = 2, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD2_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD2_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD2_B + } + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD3 + { + .module = 3, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD3_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD3_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD3_B + } + .complementary = true +#else + .complementary = false +#endif + }, +#endif +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD4 + { + .module = 4, + .used = true, + .out_a = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD4_A + }, +#ifdef CONFIG_IMXRT_FLEXPWM4_MOD3_COMP + .out_b = + { + .used = true, + .pin = GPIO_FLEXPWM4_MOD4_B + } + .complementary = true +#else + .complementary = false +#endif + }, +#endif +}; + +static struct imxrt_flexpwm_s g_pwm4 = +{ + .ops = &g_pwmops, + .modules = g_pwm4_modules, + .modules_num = 4, + .frequency = 0, + .base = IMXRT_FLEXPWM4_BASE, +}; +#endif /* CONFIG_IMXRT_FLEXPWM4 */ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pwm_change_freq + * + * Description: + * Set timer frequency and change registers value to respect that + * frequency. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_change_freq(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, uint8_t channel) +{ + FAR struct imxrt_flexpwm_s *priv = (FAR struct imxrt_flexpwm_s *)dev; +#ifdef CONFIG_PWM_MULTICHAN + uint8_t shift = info->channels[channel].channel - 1; +#else + uint8_t shift = priv->modules[0].module - 1; +#endif + uint16_t regval; + uint16_t olddiv = getreg16(priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + + MODULE_OFFSET * shift); + uint16_t newdiv = (uint32_t)((float)CLK_FREQ / info->frequency + 0.5f); + uint16_t prescale = 0; + + while (newdiv > PWM_RES && prescale < 7) + { + newdiv = newdiv >> 1; + prescale = prescale + 1; + } + + if (newdiv > PWM_RES) + { + newdiv = PWM_RES; + } + else if (newdiv < 2) + { + newdiv = 2; + } + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_CLDOK(1 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + regval = SMCTRL_FULL | SMCTRL_PRSC(prescale); + putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0CTRL_OFFSET + + MODULE_OFFSET * shift); + + putreg16(newdiv - 1, priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + + MODULE_OFFSET * shift); + + /* Update VAL0, VAL3 and VAL5 registers */ + + regval = getreg16(priv->base + IMXRT_FLEXPWM_SM0VAL0_OFFSET + + MODULE_OFFSET * shift); + regval = regval * newdiv / olddiv; + putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0VAL0_OFFSET + + MODULE_OFFSET * shift); + + regval = getreg16(priv->base + IMXRT_FLEXPWM_SM0VAL3_OFFSET + + MODULE_OFFSET * shift); + regval = regval * newdiv / olddiv; + putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0VAL3_OFFSET + + MODULE_OFFSET * shift); + + regval = getreg16(priv->base + IMXRT_FLEXPWM_SM0VAL5_OFFSET + + MODULE_OFFSET * shift); + regval = regval * newdiv / olddiv; + putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0VAL5_OFFSET + + MODULE_OFFSET * shift); + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_LDOK(1 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + return OK; +} + +/**************************************************************************** + * Name: pwm_set_output + * + * Description: + * Set duty cycle and enable PWM output. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * channel - Channel to by updated + * duty - New duty + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_set_output(FAR struct pwm_lowerhalf_s *dev, uint8_t channel, + ub16_t duty) +{ + FAR struct imxrt_flexpwm_s *priv = (FAR struct imxrt_flexpwm_s *)dev; + uint16_t period; + uint16_t width; + uint16_t regval; + double duty_pct; + uint8_t shift = channel - 1; /* Shift submodle offset addresses */ + + /* Get the period value */ + + period = getreg16(priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + + MODULE_OFFSET * shift); + + /* Compute PWM width (count value to set PWM low) */ + + duty_pct = (duty / 65536.0) * 100; + width = (uint16_t)(((uint16_t)duty_pct * period) / 100); + + /* Clear corresponding MCTRL[LDOK] bit */ + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_CLDOK(1 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + /* Write width to value register 3 and enable output A */ + + putreg16(width, priv->base + IMXRT_FLEXPWM_SM0VAL3_OFFSET + + MODULE_OFFSET * shift); + + regval = getreg16(priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + regval |= OUTEN_PWMA_EN(1 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + + /* Enable output B if complementary option is turn on */ + + if (priv->modules[shift].complementary) + { + regval = getreg16(priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + regval |= OUTEN_PWMB_EN(1 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + } + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_LDOK(1 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + return OK; +} + +/**************************************************************************** + * Name: pwm_setup + * + * Description: + * This method is called when the driver is opened. The lower half driver + * should configure and initialize the device so that it is ready for use. + * It should not, however, output pulses until the start method is called. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct imxrt_flexpwm_s *priv = (FAR struct imxrt_flexpwm_s *)dev; + uint32_t pin = 0; + uint16_t regval; + + putreg16(FCTRL0_FLVL(15), priv->base + IMXRT_FLEXPWM_FCTRL0_OFFSET); + putreg16(0x000f, priv->base + IMXRT_FLEXPWM_FSTS0_OFFSET); + putreg16(0, priv->base + IMXRT_FLEXPWM_FFILT0_OFFSET); + + for (int i = 0; i < priv->modules_num; i++) + { + /* Configure the module only if is set to be used */ + + if (priv->modules[i].used != 1) + { + continue; + } + + if (priv->modules[i].out_a.used) + { + pin = priv->modules[i].out_a.pin; + if (pin != 0) + { + imxrt_config_gpio(pin); + } + } + + /* Configure PIN_B if complementary option is turn on */ + + if (priv->modules[i].complementary) + { + if (priv->modules[i].out_b.used) + { + pin = priv->modules[i].out_b.pin; + if (pin != 0) + { + imxrt_config_gpio(pin); + } + } + } + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_CLDOK(1 << i); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + /* Set control registers 1 and 2 */ + + if (!priv->modules[i].complementary) + { + /* Enable independent PWM_A and PWM_B output */ + + regval = SMCTRL2_INDEP; + putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0CTRL2_OFFSET + + MODULE_OFFSET * i); + } + + regval = SMCTRL_FULL; /* Enable full read cycle reload */ + putreg16(regval, priv->base + IMXRT_FLEXPWM_SM0CTRL_OFFSET + + MODULE_OFFSET * i); + + /* Set output control register */ + + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0OCTRL_OFFSET + + MODULE_OFFSET * i); + + /* Set deadtime count register 0 */ + + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0DTCNT0_OFFSET + + MODULE_OFFSET * i); + + /* Set initial count register */ + + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0INIT_OFFSET + + MODULE_OFFSET * i); + + /* Set value registers */ + + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL0_OFFSET + + MODULE_OFFSET * i); + putreg16(0x82b8, priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL2_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL3_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL4_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL5_OFFSET + + MODULE_OFFSET * i); + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_LDOK(1 << i); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + regval = getreg16(priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + regval |= MCTRL_RUN(1 << i); + putreg16(regval, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + } + + return OK; +} + +/**************************************************************************** + * Name: pwm_shutdown + * + * Description: + * This method is called when the driver is closed. The lower half driver + * stop pulsed output, free any resources, disable the timer hardware, and + * put the system into the lowest possible power usage state + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct imxrt_flexpwm_s *priv = (FAR struct imxrt_flexpwm_s *)dev; + + for (int i = 0; i < priv->modules_num; i++) + { + /* Skip modules that are not used */ + + if (priv->modules[i].used != 1) + { + continue; + } + + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0CTRL_OFFSET + + MODULE_OFFSET * i); + + /* Reset fractional value registers */ + + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL0_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL1_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL2_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL3_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL4_OFFSET + + MODULE_OFFSET * i); + putreg16(0, priv->base + IMXRT_FLEXPWM_SM0VAL5_OFFSET + + MODULE_OFFSET * i); + } + + /* Stop run */ + + putreg16(0, priv->base + IMXRT_FLEXPWM_MCTRL_OFFSET); + + return OK; +} + +/**************************************************************************** + * Name: pwm_start + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) +{ + FAR struct imxrt_flexpwm_s *priv = (FAR struct imxrt_flexpwm_s *)dev; + int ret = OK; + + /* Change frequency only if it is needed */ + + if (info->frequency != priv->frequency) + { + for (int i = 0; i < PWM_NCHANNELS; i++) + { + /* Configure the module freq only if is set to be used */ + + ret = pwm_change_freq(dev, info, i); + } + + /* Save current frequency */ + + if (ret == OK) + { + priv->frequency = info->frequency; + } + } + +#ifdef CONFIG_PWM_MULTICHAN + for (int i = 0; ret == OK && i < PWM_NCHANNELS; i++) + { + /* Enable PWM output for each channel */ + + ret = pwm_set_output(dev, info->channels[i].channel, + info->channels[i].duty); + } +#else + /* Enable PWM output just for first channel */ + + ret = pwm_set_output(dev, priv->modules[0].module, info->duty); +#endif /* CONFIG_PWM_MULTICHAN */ + + return ret; +} + +/**************************************************************************** + * Name: pwm_stop + * + * Description: + * Stop the pulsed output and reset the timer resources + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * Assumptions: + * This function is called to stop the pulsed output at anytime. This + * method is also called from the timer interrupt handler when a repetition + * count expires... automatically stopping the timer. + * + ****************************************************************************/ + +static int pwm_stop(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct imxrt_flexpwm_s *priv = (FAR struct imxrt_flexpwm_s *)dev; + uint8_t shift; + uint16_t regval; + +#ifdef CONFIG_PWM_MULTICHAN + for (int i = 0; i < priv->modules_num; i++) + { + /* Skip settings if channel is not configured */ + + if (!priv->modules[i].used) + { + continue; + } + + shift = priv->modules[i].module - 1; + + regval = OUTEN_PWMA_EN(0 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + + regval = OUTEN_PWMB_EN(0 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + } +#else + shift = priv->modules[0].module - 1; + + regval = OUTEN_PWMA_EN(0 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + + regval = OUTEN_PWMB_EN(0 << shift); + putreg16(regval, priv->base + IMXRT_FLEXPWM_OUTEN_OFFSET); + +#endif /* CONFIG_PWM_MULTICHAN */ + + return OK; +} + +/**************************************************************************** + * Name: pwm_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_ioctl(FAR struct pwm_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: imxrt_pwminitialize + * + * Description: + * Initialize the PWM channel for use with the upper level PWM driver. + * + * Input Parameters: + * channel - a number identifying the PWM channel. + * + * Returned Value: + * A pointer to the lower half PWM driver is returned on success, + * NULL on failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *imxrt_pwminitialize(int pwm) +{ + FAR struct imxrt_flexpwm_s *priv; + + pwminfo("Initializing pwm %d\n", pwm); + + switch (pwm) + { +#ifdef CONFIG_IMXRT_FLEXPWM1 + case 1: + imxrt_clockall_pwm1(); + priv = &g_pwm1; + break; +#endif +#ifdef CONFIG_IMXRT_FLEXPWM2 + case 2: + imxrt_clockall_pwm2(); + priv = &g_pwm2; + break; +#endif +#ifdef CONFIG_IMXRT_FLEXPWM3 + case 3: + imxrt_clockall_pwm3(); + priv = &g_pwm3; + break; +#endif +#ifdef CONFIG_IMXRT_FLEXPWM4 + case 4: + imxrt_clockall_pwm4(); + priv = &g_pwm4; + break; +#endif + default: + pwmerr("ERROR: PWM number invalid or not configured %d\n", pwm); + return NULL; + } + + return (FAR struct pwm_lowerhalf_s *)priv; +} +#endif /* CONFIG_IMXRT_FLEXPWM */ diff --git a/arch/arm/src/imxrt/imxrt_flexpwm.h b/arch/arm/src/imxrt/imxrt_flexpwm.h new file mode 100644 index 0000000..591fd8b --- /dev/null +++ b/arch/arm/src/imxrt/imxrt_flexpwm.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * arch/arm/src/imxrt/imxrt_flexpwm.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_IMXRT_IMXRT_FLEXPWM_H +#define __ARCH_ARM_SRC_IMXRT_IMXRT_FLEXPWM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/timers/pwm.h> + +#include "hardware/imxrt_flexpwm.h" + +#ifdef CONFIG_IMXRT_FLEXPWM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Function: imxrt_pwminitialize + * + * Description: + * Initialize the PWM module for use with the upper level PWM driver. + * + * Input Parameters: + * pwm - a number identifying the PWM driver. + * + * Returned Value: + * A pointer to the lower half PWM driver is returned on success, + * NULL on failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *imxrt_pwminitialize(int pwm); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_IMXRT_FLEXPWM */ +#endif /* __ARCH_ARM_SRC_IMXRT_IMXRT_FLEXPWM_H */
