diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 5056abd44f980..9ab957be0133a 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -67,6 +67,107 @@ config IMX9_FLEXIO2_PWM_CHANNEL_PINS hex "FlexIO outputs used for FLEXIO2 timers" default 0x0000000000000000 +config IMX9_TPM_PWM + bool + select PWM_MULTICHAN + default n + +config IMX9_TPM1_PWM + depends on PWM + bool "Enable TPM1 based PWM generation" + select IMX9_TPM_PWM + default n + +config IMX9_TPM1_PWM_NCHANNELS + depends on IMX9_TPM1_PWM + int "Number of channels for TPM1" + default 1 + +config IMX9_TPM1_PWM_CHMUX + depends on IMX9_TPM1_PWM + hex "Channel mux for TPM1" + default 0x03020100 + +config IMX9_TPM2_PWM + depends on PWM + bool "Enable TPM2 based PWM generation" + select IMX9_TPM_PWM + default n + +config IMX9_TPM2_PWM_NCHANNELS + depends on IMX9_TPM2_PWM + int "Number of channels for TPM2" + default 1 + +config IMX9_TPM2_PWM_CHMUX + depends on IMX9_TPM2_PWM + hex "Channel mux for TPM2" + default 0x03020100 + +config IMX9_TPM3_PWM + depends on PWM + bool "Enable TPM3 based PWM generation" + select IMX9_TPM_PWM + default n + +config IMX9_TPM3_PWM_NCHANNELS + depends on IMX9_TPM3_PWM + int "Number of channels for TPM3" + default 1 + +config IMX9_TPM3_PWM_CHMUX + depends on IMX9_TPM3_PWM + hex "Channel mux for TPM3" + default 0x03020100 + +config IMX9_TPM4_PWM + depends on PWM + bool "Enable TPM4 based PWM generation" + select IMX9_TPM_PWM + default n + +config IMX9_TPM4_PWM_NCHANNELS + depends on IMX9_TPM4_PWM + int "Number of channels for TPM4" + default 1 + +config IMX9_TPM4_PWM_CHMUX + depends on IMX9_TPM4_PWM + hex "Channel mux for TPM4" + default 0x03020100 + +config IMX9_TPM5_PWM + depends on PWM + bool "Enable TPM5 based PWM generation" + select IMX9_TPM_PWM + default n + +config IMX9_TPM5_PWM_NCHANNELS + depends on IMX9_TPM5_PWM + int "Number of channels for TPM5" + default 1 + +config IMX9_TPM5_PWM_CHMUX + depends on IMX9_TPM5_PWM + hex "Channel mux for TPM5" + default 0x03020100 + +config IMX9_TPM6_PWM + depends on PWM + bool "Enable TPM6 based PWM generation" + select IMX9_TPM_PWM + default n + +config IMX9_TPM6_PWM_NCHANNELS + depends on IMX9_TPM6_PWM + int "Number of channels for TPM6" + default 1 + +config IMX9_TPM6_PWM_CHMUX + depends on IMX9_TPM6_PWM + hex "Channel mux for TPM6" + default 0x03020100 + config IMX9_USBDEV bool default n diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index 8b74632163550..0d7427aec3eb0 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -39,6 +39,10 @@ ifeq ($(CONFIG_IMX9_FLEXIO_PWM),y) CHIP_CSRCS += imx9_flexio_pwm.c endif +ifeq ($(CONFIG_IMX9_TPM_PWM),y) + CHIP_CSRCS += imx9_tpm_pwm.c +endif + ifeq ($(CONFIG_IMX9_USBDEV),y) CHIP_CSRCS += imx9_usbdev.c endif diff --git a/arch/arm64/src/imx9/hardware/imx9_tpm.h b/arch/arm64/src/imx9/hardware/imx9_tpm.h new file mode 100644 index 0000000000000..0b6c3b5dc84eb --- /dev/null +++ b/arch/arm64/src/imx9/hardware/imx9_tpm.h @@ -0,0 +1,206 @@ +/**************************************************************************** + * arch/arm64/src/imx9/hardware/imx9_tpm.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_ARM64_SRC_IMX9_HARDWARE_IMX9_TPM_H +#define __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_TPM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +#define IMX9_TPM_VERID_OFFSET 0x0000 /* Version ID */ +#define IMX9_TPM_PARAM_OFFSET 0x0004 /* Parameter */ +#define IMX9_TPM_GLOBAL_OFFSET 0x0008 /* TPM Global */ +#define IMX9_TPM_SC_OFFSET 0x0010 /* Status and Control */ +#define IMX9_TPM_CNT_OFFSET 0x0014 /* Counter */ +#define IMX9_TPM_MOD_OFFSET 0x0018 /* Modulo */ +#define IMX9_TPM_STATUS_OFFSET 0x001c /* Capture and Compare Status */ +#define IMX9_TPM_CXSC_OFFSET(ch) (0x0020 + (ch) * 8) /* Channel Status and Control */ +#define IMX9_TPM_CXV_OFFSET(ch) (0x0024 + (ch) * 8) /* Channel Value */ +#define IMX9_TPM_C1SC_OFFSET 0x0028 /* Channel n Status and Control */ +#define IMX9_TPM_C1V_OFFSET 0x002c /* Channel n Value */ +#define IMX9_TPM_C2SC_OFFSET 0x0030 /* Channel n Status and Control */ +#define IMX9_TPM_C2V_OFFSET 0x0034 /* Channel n Value */ +#define IMX9_TPM_C3SC_OFFSET 0x0038 /* Channel n Status and Control */ +#define IMX9_TPM_C3V_OFFSET 0x003c /* Channel n Value */ +#define IMX9_TPM_COMBINE_OFFSET 0x0064 /* Combine Channel */ +#define IMX9_TPM_TRIG_OFFSET 0x006c /* Channel Trigger */ +#define IMX9_TPM_POL_OFFSET 0x0070 /* Channel Polarity */ +#define IMX9_TPM_FILTER_OFFSET 0x0078 /* Filter Control */ +#define IMX9_TPM_QDCTRL_OFFSET 0x0080 /* Quadrature Decoder Control and Status */ +#define IMX9_TPM_CONF_OFFSET 0x0084 /* Configuration */ + +/* Register Bitfield Definitions ********************************************/ + +/* PARAM */ + +#define TPM_PARAM_WIDTH_SHIFT (16) /* Bit[23:16]: Width of the counter and timer channels */ +#define TPM_PARAM_WIDTH_MASK (0xff << TPM_PARAM_WIDTH_SHIFT) + +#define TPM_PARAM_TRIG_SHIFT (8) /* Bit[15:8]: Number of triggers that TPM implements */ +#define TPM_PARAM_TRIG_MASK (0xff << LPIT_PARAM_TRIG_SHIFT) + +#define TPM_PARAM_CHAN_SHIFT (0) /* Bit[7:0]: Number of timer channels */ +#define TPM_PARAM_CHAN_MASK (0xff << TPM_PARAM_CHAN_SHIFT) + +/* GLOBAL */ + +#define TPM_GLOBAL_RST_SHIFT (1) /* Bit[1]: Software Reset */ +#define TPM_GLOBAL_RST_MASK (0x1 << TPM_GLOBAL_RST_SHIFT) + +#define TPM_GLOBAL_NOUPDATE_SHIFT (0) /* Bit[0]: Block updates to internal registers */ +#define TPM_GLOBAL_NOUPDATE_MASK (0x1 << TPM_GLOBAL_NOUPDATE_SHIFT) + +/* SC */ + +#define TPM_SC_DMA_SHIFT (8) /* Bit[8]: DMA Enable */ +#define TPM_SC_DMA_MASK (0x1 << TPM_SC_DMA_SHIFT) + +#define TPM_SC_TOF_SHIFT (7) /* Bit[7]: Timer Overflow Flag */ +#define TPM_SC_TOF_MASK (0x1 << TPM_SC_TOF_SHIFT) + +#define TPM_SC_TOIE_SHIFT (6) /* Bit[6]: Timer Overflow Interrupt Enable */ +#define TPM_SC_TOIE_MASK (0x1 << TPM_SC_TOIE_SHIFT) + +#define TPM_SC_CPWMS_SHIFT (5) /* Bit[5]: Center-Aligned PWM Select */ +#define TPM_SC_CPWMS_MASK (0x1 << TPM_SC_CPWMS_SHIFT) + +#define TPM_SC_CMOD_SHIFT (3) /* Bit[4:3]: Clock Mode Selection */ +#define TPM_SC_CMOD_MASK (0x3 << TPM_SC_CMOD_SHIFT) + +#define TPM_SC_PS_SHIFT (0) /* Bit[2:0]: Prescale Factor Selection */ +#define TPM_SC_PS_MASK (0x7 << TPM_SC_PS_SHIFT) + +/* STATUS */ + +#define TPM_STATUS_TOF_SHIFT (8) /* Bit[8]: Timer Overflow Flag */ +#define TPM_STATUS_TOF_MASK (0x1 << TPM_STATUS_TOF_SHIFT) + +#define TPM_STATUS_CH3F_SHIFT (3) /* Bit[3]: Channel 3 Flag */ +#define TPM_STATUS_CH3F_MASK (0x1 << TPM_STATUS_CH3F_SHIFT) + +#define TPM_STATUS_CH2F_SHIFT (2) /* Bit[2]: Channel 2 Flag */ +#define TPM_STATUS_CH2F_MASK (0x1 << TPM_STATUS_CH2F_SHIFT) + +#define TPM_STATUS_CH1F_SHIFT (1) /* Bit[1]: Channel 1 Flag */ +#define TPM_STATUS_CH1F_MASK (0x1 << TPM_STATUS_CH1F_SHIFT) + +#define TPM_STATUS_CH0F_SHIFT (0) /* Bit[0]: Channel 0 Flag */ +#define TPM_STATUS_CH0F_MASK (0x1 << TPM_STATUS_CH0F_SHIFT) + +/* C0SC - C3SC */ + +#define TPM_CXSC_CHF_SHIFT (7) /* Bit[7]: Channel Flag */ +#define TPM_CXSC_CHF_MASK (0x1 << TPM_CXSC_CHF_SHIFT) + +#define TPM_CXSC_CHIE_SHIFT (6) /* Bit[6]: Channel Interrupt Enable */ +#define TPM_CXSC_CHIE_MASK (0x1 << TPM_CXSC_CHIE_SHIFT) + +#define TPM_CXSC_MSB_SHIFT (5) /* Bit[5]: Channel Mode Select B */ +#define TPM_CXSC_MSB_MASK (0x1 << TPM_CXSC_MSB_SHIFT) + +#define TPM_CXSC_MSA_SHIFT (4) /* Bit[4]: Channel Mode Select A */ +#define TPM_CXSC_MSA_MASK (0x1 << TPM_CXSC_MSA_SHIFT) + +#define TPM_CXSC_ELSB_SHIFT (3) /* Bit[3]: Edge or Level Select B */ +#define TPM_CXSC_ELSB_MASK (0x1 << TPM_CXSC_ELSB_SHIFT) + +#define TPM_CXSC_ELSA_SHIFT (2) /* Bit[2]: Edge or Level Select A */ +#define TPM_CXSC_ELSA_MASK (0x1 << TPM_CXSC_ELSA_SHIFT) + +#define TPM_CXSC_DMA_SHIFT (0) /* Bit[0]: DMA Enable */ +#define TPM_CXSC_DMA_MASK (0x1 << TPM_CXSC_DMA_SHIFT) + +/* COMBINE */ + +#define TPM_COMBINE_COMSWAP1_SHIFT (9) /* Bit[9]: Combine Channels 2 and 3 Swap */ +#define TPM_COMBINE_COMSWAP1_MASK (0x1 << TPM_COMBINE_COMSWAP1_SHIFT) + +#define TPM_COMBINE_COMBINE1_SHIFT (8) /* Bit[8]: Combine Channels 2 and 3 */ +#define TPM_COMBINE_COMBINE1_MASK (0x1 << TPM_COMBINE_COMBINE1_SHIFT) + +#define TPM_COMBINE_COMSWAP0_SHIFT (1) /* Bit[1]: Combine Channel 0 and 1 Swap */ +#define TPM_COMBINE_COMSWAP0_MASK (0x1 << TPM_COMBINE_COMSWAP0_SHIFT) + +#define TPM_COMBINE_COMBINE0_SHIFT (0) /* Bit[0]: Combine Channels 0 and 1 */ +#define TPM_COMBINE_COMBINE0_MASK (0x1 << TPM_COMBINE_COMBINE0_SHIFT) + +/* TRIG */ + +#define TPM_TRIG_TRIGX_MASK(ch) (0x1 << (ch)) /* Channel trigger configure */ + +/* POL */ + +#define TPM_POL_POLX_MASK(ch) (0x1 < (ch)) /* Channel polarity active low */ + +/* FILTER */ + +#define TPM_FILTER_CHXFVAL_MASK(ch) (0xf << ((ch) * 4))) /* Channel filter value */ + +/* QDCTRL */ + +#define TPM_QDCTRL_QUADMODE_SHIFT (3) /* Bit[3]: Quadrature Decoder Mode */ +#define TPM_QDCTRL_QUADMODE_MASK (0x1 << TPM_QDCTRL_QUADMODE_SHIFT) + +#define TPM_QDCTRL_QUADIR_SHIFT (2) /* Bit[2]: Counter Direction */ +#define TPM_QDCTRL_QUADIR_MASK (0x1 << TPM_QDCTRL_QUADIR_SHIFT) + +#define TPM_QDCTRL_TOFDIR_SHIFT (1) /* Bit[1]: Timer Overflow Direction */ +#define TPM_QDCTRL_TOFDIR_MASK (0x1 << TPM_QDCTRL_TOFDIR_SHIFT) + +#define TPM_QDCTRL_QUADEN_SHIFT (0) /* Bit[0]: Quadrature Decoder Enable */ +#define TPM_QDCTRL_QUADEN_MASK (0x1 << TPM_QDCTRL_QUADEN_SHIFT) + +/* CONF */ + +#define TPM_CONF_TRGSEL_SHIFT (24) /* Bit[25:24]: Trigger Select */ +#define TPM_CONF_TRGSEL_MASK (0x3 << TPM_CONF_TRGSEL_SHIFT) + +#define TPM_CONF_TRGSRC_SHIFT (23) /* Bit[23]: Trigger Source select */ +#define TPM_CONF_TRGSRC_MASK (0x1 << TPM_CONF_TRGSRC_SHIFT) + +#define TPM_CONF_TRGPOL_SHIFT (22) /* Bit[22]: Trigger Polarity */ +#define TPM_CONF_TRGPOL_MASK (0x1 << TPM_CONF_TRGPOL_SHIFT) + +#define TPM_CONF_CPOT_SHIFT (19) /* Bit[19]: Counter Pause on Trigger */ +#define TPM_CONF_CPOT_MASK (0x1 << TPM_CONF_CPOT_SHIFT) + +#define TPM_CONF_CROT_SHIFT (18) /* Bit[18]: Counter Reload on Trigger */ +#define TPM_CONF_CROT_MASK (0x1 << TPM_CONF_CROT_SHIFT) + +#define TPM_CONF_CSOO_SHIFT (17) /* Bit[17]: Counter Stop on Overflow */ +#define TPM_CONF_CSOO_MASK (0x1 << TPM_CONF_CSOO_SHIFT) + +#define TPM_CONF_CSOT_SHIFT (16) /* Bit[16]: Counter Start on Trigger */ +#define TPM_CONF_CSOT_MASK (0x1 << TPM_CONF_CSOT_SHIFT) + +#define TPM_CONF_DBGMODE_SHIFT (6) /* Bit[7:6]: Debug Mode */ +#define TPM_CONF_DBGMODE_MASK (0x3 << TPM_CONF_DBGMODE_SHIFT) + +#define TPM_CONF_DOZEEN_SHIFT (5) /* Bit[5]: Doze Enable */ +#define TPM_CONF_DOZEEN_MASK (0x1 << TPM_CONF_DOZEEN_SHIFT) + +#endif /* __ARCH_ARM64_SRC_IMX9_HARDWARE_IMX9_TPM_H */ diff --git a/arch/arm64/src/imx9/imx9_tpm_pwm.c b/arch/arm64/src/imx9/imx9_tpm_pwm.c new file mode 100644 index 0000000000000..8194ac6ea11bd --- /dev/null +++ b/arch/arm64/src/imx9/imx9_tpm_pwm.c @@ -0,0 +1,693 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_tpm_pwm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "arm64_arch.h" +#include "imx9_tpm_pwm.h" +#include "imx9_iomuxc.h" +#include "imx9_ccm.h" + +#include "hardware/imx9_iomuxc.h" +#include "hardware/imx9_pinmux.h" +#include "hardware/imx9_ccm.h" + +#ifdef CONFIG_IMX9_TPM_PWM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CHMUX(priv, x) ((((priv)->chmux) >> (x)) & 0xff) + +/* This is a temporary shortcut to configure TPM1 and TPM3 + * frequencies correctly; they don't have an own root clock + * + * TODO: Determine the frequencies in a more proper way + */ + +#define AON_CLK_FREQ 133333333 +#define WAKEUP_CLK_FREQ 133333333 +#define OSC_24_CLK_FREQ 24000000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure represents the state of one PWM timer */ + +struct imx9_pwmtimer_s +{ + const struct pwm_ops_s *ops; /* PWM operations */ + const tpm_pwm_id_t id; /* PWM_TPM1...PWM_TPM6 */ + const uintptr_t base; /* The base address of the TPM */ + const int n_channels; /* Number of channels used for TPM block */ + const uint32_t chmux; /* Additional muxing of TPM outputs */ + const int clk; /* Input clock frequency for the TPM */ + uint32_t period; + unsigned frequency; /* Current frequency setting */ +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ + +/* PWM driver methods */ + +static int pwm_setup(struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(struct pwm_lowerhalf_s *dev); + +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info); + +static int pwm_stop(struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the list of lower half PWM driver methods used by the upper half + * driver + */ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl, +}; + +static struct imx9_pwmtimer_s g_pwmdev[] = +{ +#ifdef CONFIG_IMX9_TPM1_PWM + { + .ops = &g_pwmops, + .id = PWM_TPM1, + .base = IMX9_TPM1_BASE, + .n_channels = CONFIG_IMX9_TPM1_PWM_NCHANNELS, + .chmux = CONFIG_IMX9_TPM1_PWM_CHMUX, + .clk = AON_CLK_FREQ, + }, +#endif + +#ifdef CONFIG_IMX9_TPM2_PWM + { + .ops = &g_pwmops, + .id = PWM_TPM2, + .base = IMX9_TPM2_BASE, + .n_channels = CONFIG_IMX9_TPM2_PWM_NCHANNELS, + .chmux = CONFIG_IMX9_TPM2_PWM_CHMUX, + .clk = OSC_24_CLK_FREQ, + }, +#endif + +#ifdef CONFIG_IMX9_TPM3_PWM + { + .ops = &g_pwmops, + .id = PWM_TPM3, + .base = IMX9_TPM3_BASE, + .n_channels = CONFIG_IMX9_TPM3_PWM_NCHANNELS, + .chmux = CONFIG_IMX9_TPM3_PWM_CHMUX, + .clk = WAKEUP_CLK_FREQ, + }, +#endif + +#ifdef CONFIG_IMX9_TPM4_PWM + { + .ops = &g_pwmops, + .id = PWM_TPM4, + .base = IMX9_TPM4_BASE, + .n_channels = CONFIG_IMX9_TPM4_PWM_NCHANNELS, + .chmux = CONFIG_IMX9_TPM4_PWM_CHMUX, + .clk = OSC_24_CLK_FREQ, + }, +#endif + +#ifdef CONFIG_IMX9_TPM5_PWM + { + .ops = &g_pwmops, + .id = PWM_TPM5, + .base = IMX9_TPM5_BASE, + .n_channels = CONFIG_IMX9_TPM5_PWM_NCHANNELS, + .chmux = CONFIG_IMX9_TPM5_PWM_CHMUX, + .clk = OSC_24_CLK_FREQ, + }, +#endif + +#ifdef CONFIG_IMX9_TPM6_PWM + { + .ops = &g_pwmops, + .id = PWM_TPM6, + .base = IMX9_TPM6_BASE, + .n_channels = CONFIG_IMX9_TPM6_PWM_NCHANNELS, + .chmux = CONFIG_IMX9_TPM6_PWM_CHMUX, + .clk = OSC_24_CLK_FREQ, + }, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pwm_getreg + * + * Description: + * Read the value of an PWM timer register. + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static inline uint32_t pwm_getreg(struct imx9_pwmtimer_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: pwm_putreg + * + * Description: + * Read the value of an PWM timer register. + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void pwm_putreg(struct imx9_pwmtimer_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: tpm_mux + * + * Description: + * Mux the tpm output pins to pads. The macros TPMn_PWMx_MUX + * need to be defined in the board.h file + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void tpm_mux(void) +{ +#ifdef CONFIG_IMX9_TPM1_PWM +# ifdef TPM1_PWM0_MUX + imx9_iomux_configure(TPM1_PWM0_MUX); +# endif + +# ifdef TPM1_PWM1_MUX + imx9_iomux_configure(TPM1_PWM1_MUX); +# endif + +# ifdef TPM1_PWM2_MUX + imx9_iomux_configure(TPM1_PWM2_MUX); +# endif + +# ifdef TPM1_PWM3_MUX + imx9_iomux_configure(TPM1_PWM3_MUX); +# endif +#endif + +#ifdef CONFIG_IMX9_TPM2_PWM +# ifdef TPM2_PWM0_MUX + imx9_iomux_configure(TPM2_PWM0_MUX); +# endif + +# ifdef TPM2_PWM1_MUX + imx9_iomux_configure(TPM2_PWM1_MUX); +# endif + +# ifdef TPM2_PWM2_MUX + imx9_iomux_configure(TPM2_PWM2_MUX); +# endif + +# ifdef TPM2_PWM3_MUX + imx9_iomux_configure(TPM2_PWM3_MUX); +# endif +#endif + +#ifdef CONFIG_IMX9_TPM3_PWM +# ifdef TPM3_PWM0_MUX + imx9_iomux_configure(TPM3_PWM0_MUX); +# endif + +# ifdef TPM3_PWM1_MUX + imx9_iomux_configure(TPM3_PWM1_MUX); +# endif + +# ifdef TPM3_PWM2_MUX + imx9_iomux_configure(TPM3_PWM2_MUX); +# endif + +# ifdef TPM3_PWM3_MUX + imx9_iomux_configure(TPM3_PWM3_MUX); +# endif +#endif + +#ifdef CONFIG_IMX9_TPM4_PWM +# ifdef TPM4_PWM0_MUX + imx9_iomux_configure(TPM4_PWM0_MUX); +# endif + +# ifdef TPM4_PWM1_MUX + imx9_iomux_configure(TPM4_PWM1_MUX); +# endif + +# ifdef TPM4_PWM2_MUX + imx9_iomux_configure(TPM4_PWM2_MUX); +# endif + +# ifdef TPM4_PWM3_MUX + imx9_iomux_configure(TPM4_PWM3_MUX); +# endif +#endif + +#ifdef CONFIG_IMX9_TPM5_PWM +# ifdef TPM5_PWM0_MUX + imx9_iomux_configure(TPM5_PWM0_MUX); +# endif + +# ifdef TPM5_PWM1_MUX + imx9_iomux_configure(TPM5_PWM1_MUX); +# endif + +# ifdef TPM5_PWM2_MUX + imx9_iomux_configure(TPM5_PWM2_MUX); +# endif + +# ifdef TPM5_PWM3_MUX + imx9_iomux_configure(TPM5_PWM3_MUX); +# endif +#endif + +#ifdef CONFIG_IMX9_TPM6_PWM +# ifdef TPM6_PWM0_MUX + imx9_iomux_configure(TPM6_PWM0_MUX); +# endif + +# ifdef TPM6_PWM1_MUX + imx9_iomux_configure(TPM6_PWM1_MUX); +# endif + +# ifdef TPM6_PWM2_MUX + imx9_iomux_configure(TPM6_PWM2_MUX); +# endif + +# ifdef TPM6_PWM3_MUX + imx9_iomux_configure(TPM6_PWM3_MUX); +# endif +#endif +} + +/**************************************************************************** + * Name: pwm_update_frequency + * + * Description: + * Initialize the timer trigger, generating the PWM freuency + * + * Input Parameters: + * priv - A reference to the lower half PWM driver state structure + * freq - The requested PWM frequency for this flexio block + * + * Returned Value: + * Zero on success, negated error value on failure + * + ****************************************************************************/ + +static int pwm_update_frequency(struct imx9_pwmtimer_s *priv, int freq) +{ + int i; + + if (freq != priv->frequency) + { + /* Start PWM on all channels if it was previously stopped */ + + if (freq > 0 && priv->frequency == 0) + { + /* Set the EPWM mode for all the configured channels: + * + * High-true pulses (clear output on counter match, set output + * on counter reload, clear output when counter first enabled + * or paused) + * SC[CPWMS] = 0, MSnB:MSnA = 1:0 ELSnB:ELSnA = 0 + */ + + for (i = 0; i < priv->n_channels; i++) + { + pwm_putreg(priv, IMX9_TPM_CXSC_OFFSET(CHMUX(priv, i)), + TPM_CXSC_MSB_MASK); + } + } + + priv->period = freq > 0 ? priv->clk / freq : 0; + + pwminfo("PWM%d frequency: %" PRIu32" period: %" PRIu32 "\n", priv->id, + freq, priv->period); + + /* Set the period */ + + pwm_putreg(priv, IMX9_TPM_MOD_OFFSET, priv->period); + + priv->frequency = freq; + } + + return OK; +} + +/**************************************************************************** + * Name: pwm_update_duty + * + * Description: + * Change the channel duty cycle. + * + * Input Parameters: + * priv - A reference to the lower half PWM driver state structure + * period - PWM pulse width in timer ticks + * channel - Channel to by updated + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_update_duty(struct imx9_pwmtimer_s *priv, int pwm_ch, + ub16_t duty16) +{ + uint64_t duty = duty16 & 0xffffffff; + uint32_t edge = (duty * priv->period + 0x8000) >> 16; + int timer = pwm_ch - 1; + + if (pwm_ch == 0 || timer > priv->n_channels) + { + pwmerr("ERROR: PWM%d has no such channel: %d\n", priv->id, timer); + return -EINVAL; + } + + pwminfo("PWM%d channel %d, p: %d e: %" PRIu32 "\n", priv->id, + CHMUX(priv, timer), priv->period, edge); + + pwm_putreg(priv, IMX9_TPM_CXV_OFFSET(CHMUX(priv, timer)), edge); + + 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 + * + * Assumptions: + * + ****************************************************************************/ + +static int pwm_setup(struct pwm_lowerhalf_s *dev) +{ + 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(struct pwm_lowerhalf_s *dev) +{ + /* Make sure that the output has been stopped */ + + pwm_stop(dev); + + 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(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info) +{ + struct imx9_pwmtimer_s *priv = (struct imx9_pwmtimer_s *)dev; + int ret = OK; + int i; + + if (priv == NULL || info == NULL || info->frequency == 0) + { + return -EINVAL; + } + + /* Set the frequency if not changed */ + + if (pwm_update_frequency(priv, info->frequency) == OK) + { + /* Handle channel specific setup */ + + for (i = 0; i < CONFIG_PWM_NCHANNELS; i++) + { + if (ret != OK || info->channels[i].channel == -1) + { + break; + } + + pwm_update_duty(priv, info->channels[i].channel, + info->channels[i].duty); + } + } + + 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. + * + ****************************************************************************/ + +static int pwm_stop(struct pwm_lowerhalf_s *dev) +{ + struct imx9_pwmtimer_s *priv = (struct imx9_pwmtimer_s *)dev; + int i; + + pwminfo("PWM%d stop\n", priv->id); + + /* Check that timer is valid */ + + if (priv == NULL) + { + return -EINVAL; + } + + /* Disable the channels */ + + for (i = 0; i < priv->n_channels; i++) + { + pwm_putreg(priv, IMX9_TPM_CXSC_OFFSET(CHMUX(priv, i)), 0); + } + + /* Set frequency to 0 */ + + pwm_update_frequency(priv, 0); + + 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(struct pwm_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_tpm_pwm_init + * + * Description: + * Initialize tpm blocks to generate EPWM. + * + * Input Parameters: + * pwmid - A number identifying the pwm block. The number of valid + * IDs varies depending on the configuration. + * + * Returned Value: + * On success, a pointer to the lower half PWM driver is + * returned. NULL is returned on any failure. + * + ****************************************************************************/ + +struct pwm_lowerhalf_s *imx9_tpm_pwm_init(tpm_pwm_id_t pwmid) +{ + struct imx9_pwmtimer_s *lower = NULL; + int i; + + for (i = 0; i < sizeof(g_pwmdev) / sizeof(g_pwmdev[0]); i++) + { + if (pwmid == g_pwmdev[i].id) + { + lower = &g_pwmdev[i]; + break; + } + } + + if (lower) + { + /* IO mux */ + + tpm_mux(); + + /* Reset TPM */ + + pwm_putreg(lower, IMX9_TPM_GLOBAL_OFFSET, TPM_GLOBAL_RST_MASK); + pwm_putreg(lower, IMX9_TPM_GLOBAL_OFFSET, 0); + while (pwm_getreg(lower, IMX9_TPM_GLOBAL_OFFSET) != 0); + + /* TPM 1 is always clocked by AON bus and TPM3 by WAKEUP bus */ + + if (pwmid != PWM_TPM1 && pwmid != PWM_TPM3) + { + /* 24 MHz source clock */ + + imx9_ccm_configure_root_clock(CCM_CR_TPM1 + pwmid, OSC_24M, 1); + + /* Enable peripheral clock */ + + imx9_ccm_gate_on(CCM_LPCG_TPM1 + pwmid, true); + } + + /* Set status and control: + * CMOD = 1 (increment on every clock) + * PS = 0 (clock divider 1) + */ + + pwm_putreg(lower, IMX9_TPM_SC_OFFSET, 1 << TPM_SC_CMOD_SHIFT); + + pwminfo("PWM%d at %" PRIxPTR " configured\n", pwmid, lower->base); + } + else + { + pwmerr("ERROR: No such timer configured %d\n", pwmid); + } + + return (struct pwm_lowerhalf_s *)lower; +} + +#endif diff --git a/arch/arm64/src/imx9/imx9_tpm_pwm.h b/arch/arm64/src/imx9/imx9_tpm_pwm.h new file mode 100644 index 0000000000000..f6bdfde28e701 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_tpm_pwm.h @@ -0,0 +1,97 @@ +/**************************************************************************** + * arch/arm64/src/imx9/imx9_tpm_pwm.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_ARM64_SRC_IMX9_IMX9_TPM_PWM_H +#define __ARCH_ARM64_SRC_IMX9_IMX9_TPM_PWM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include "hardware/imx9_tpm.h" + +/* Check if PWM support for any channel is enabled. */ + +#ifdef CONFIG_IMX9_TPM_PWM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + PWM_TPM1 = 0, + PWM_TPM2 = 1, + PWM_TPM3 = 2, + PWM_TPM4 = 3, + PWM_TPM5 = 4, + PWM_TPM6 = 5, +} tpm_pwm_id_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: imx9_tpm_pwm_init + * + * Description: + * Initialize a TPM block for EPWM usage. + * + * Input Parameters: + * pwmid - A number identifying the pwm block. + * + * Returned Value: + * On success, a pointer to the lower half of the PWM driver is + * returned. NULL is returned on any failure. + * + ****************************************************************************/ + +struct pwm_lowerhalf_s *imx9_tpm_pwm_init(tpm_pwm_id_t pwmid); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_IMX9_TPM_PWM */ +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_TPM_PWM_H */