From da36426758f6d2f5e4f86be249d1e641faa1cf62 Mon Sep 17 00:00:00 2001 From: Jukka Laitinen Date: Tue, 26 Mar 2024 14:58:09 +0200 Subject: [PATCH] arch/arm64/src/imx9: Add TPM based PWM driver for IMX9 Signed-off-by: Jukka Laitinen --- arch/arm64/src/imx9/Kconfig | 18 + arch/arm64/src/imx9/Make.defs | 4 + arch/arm64/src/imx9/hardware/imx9_tpm.h | 206 +++++++++++ arch/arm64/src/imx9/imx9_tpm_pwm.c | 447 ++++++++++++++++++++++++ arch/arm64/src/imx9/imx9_tpm_pwm.h | 98 ++++++ 5 files changed, 773 insertions(+) create mode 100644 arch/arm64/src/imx9/hardware/imx9_tpm.h create mode 100644 arch/arm64/src/imx9/imx9_tpm_pwm.c create mode 100644 arch/arm64/src/imx9/imx9_tpm_pwm.h diff --git a/arch/arm64/src/imx9/Kconfig b/arch/arm64/src/imx9/Kconfig index 3c921bbebe7c5..02c590167be67 100644 --- a/arch/arm64/src/imx9/Kconfig +++ b/arch/arm64/src/imx9/Kconfig @@ -16,16 +16,34 @@ config ARCH_CHIP_IMX93 select ARCH_HAVE_MULTICPU select ARMV8A_HAVE_GICv3 select ARCH_CORTEX_A55 + select ARCH_HAVE_PWM_MULTICHAN endchoice # i.MX9 Chip Selection endmenu # "i.MX9 Chip Selection" +config IMX9_TPM_PWM + bool + select PWM_MULTICHAN + default n + menu "i.MX9 Peripheral Selection" config IMX9_UART1 bool "UART1" default n select UART1_SERIALDRIVER + +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 + endmenu # iMX Peripheral Selection config IMX9_PLL diff --git a/arch/arm64/src/imx9/Make.defs b/arch/arm64/src/imx9/Make.defs index 8e8a8d98623be..8c74f82138820 100644 --- a/arch/arm64/src/imx9/Make.defs +++ b/arch/arm64/src/imx9/Make.defs @@ -30,3 +30,7 @@ ifeq ($(CONFIG_ARCH_CHIP_IMX93),y) CHIP_ASRCS = imx93_lowputc.S endif endif + +ifeq ($(CONFIG_IMX9_TPM_PWM),y) + CHIP_CSRCS += imx9_tpm_pwm.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..4660b05905d4a --- /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..cf8f082a7b771 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_tpm_pwm.c @@ -0,0 +1,447 @@ +/**************************************************************************** + * 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 "hardware/imx9_ccm.h" +#include "imx9_tpm_pwm.h" + +#ifdef CONFIG_IMX9_TPM_PWM + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure represents the state of one PWM timer */ + +struct imx9_pwmtimer_s +{ + const struct pwm_ops_s *ops; /* PWM operations */ + int nchannels; /* Number of channels used for TPM block */ + unsigned frequency; /* Current frequency setting */ + int clock; /* Clock selection for the TPM block */ + int cgate; /* Clock gate selection for the TPM block */ + uintptr_t base; /* The base address of the pwm block */ +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ + +/* Register access */ + +static uint32_t pwm_getreg(struct imx9_pwmtimer_s *priv, int offset); +static void pwm_putreg(struct imx9_pwmtimer_s *priv, int offset, + uint32_t value); + +/* 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_TPM3_PWM + { + .ops = &g_pwmops, + .nchannels = CONFIG_IMX9_TPM3_PWM_NCHANNELS, + .clock = CCM_CR_TPM1, + .cgate = CCM_LPCG_TPM3, + .base = IMX9_TPM3_BASE, + }, +#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) +{ + /* TODO: 8,16 & 32 bit reg width consideration + * 32 bit access is required for a 32 bit register + */ + + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * 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 + * duty - New duty cycle as fraction of 65536 + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_update_duty(struct imx9_pwmtimer_s *priv, uint32_t period, + uint8_t channel, ub16_t duty16) +{ + uint64_t duty = ub16toui(duty16); + uint32_t edge = (duty * period + 0x8000) >> 16; + + if (channel == 0 || channel > priv->nchannels) + { + pwmerr("ERROR: PWM has no such channel: %u\n", channel); + return -EINVAL; + } + + pwminfo("PWM %p channel %u, p: %d e: %d\n", priv, channel, period, edge); + + pwm_putreg(priv, IMX9_TPM_CXV_OFFSET(channel), 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 i; + uint32_t period; + bool first; + + if (priv == NULL || info == NULL || info->frequency == 0) + { + return -EINVAL; + } + + /* Check if this is the first call after being stopped */ + + first = priv->frequency == 0; + + /* We run on 24MHz OSC clock */ + + period = 24000000 / info->frequency; + + if (info->frequency != priv->frequency) + { + pwminfo("PWM frequency: %" PRIu32" period: %" PRIu32 "\n", + info->frequency, period); + + /* Set period */ + + pmw_putreg(IMX9_TPM_MOD(priv->base), period); + + priv->frequency = info->frequency; + } + + /* Handle channel specific setup */ + + for (i = 0; i < priv->nchannels; i++) + { + if (info->channels[i].channel == -1) + { + break; + } + + pwm_update_duty(priv, period, i, info->channels[i].duty); + + /* Set EPWM mode for channel : + * + * 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 + */ + + if (first) + { + pwm_putreg(priv, IMX9_TPM_CXSC_OFFSET(i), TPM_CXSC_MSB_MASK); + } + } + + return OK; +} + +/**************************************************************************** + * 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 %" PRIuPTR " pwm_stop\n", priv->base); + + /* Check that timer is valid */ + + if (priv == NULL) + { + return -EINVAL; + } + + /* Stopped so set frequency to zero */ + + priv->frequency = 0; + + /* Disable the channels */ + + for (i = 0; i < priv->nchannels; i++) + { + pwm_putreg(priv, IMX9_TPM_CXSC_OFFSET(i), 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(int pwmid) +{ + struct imx9_pwmtimer_s *lower; + + if (pwmid >= sizeof(g_pwmdev) / sizeof(struct imx9_pwmtimer_s)) + { + pwmerr("ERROR: No such timer configured %d\n", pwmid); + lower = NULL; + } + else + { + lower = &g_pwmdev[pwmid]; + } + + if (lower) + { + /* 24 MHz source clock */ + + imx9_ccm_configure_root_clock(lower->clock, OSC_24M, 1); + + /* Enable peripheral clock */ + + imx9_ccm_gate_on(lower->cgate, true); + + pwminfo("PWM%u at %" PRIuPTR " configured\n", pwmid, lower->base); + } + + 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..c38c071ba9c67 --- /dev/null +++ b/arch/arm64/src/imx9/imx9_tpm_pwm.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * 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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Check if PWM support for any channel is enabled. */ + +#ifdef CONFIG_IMX9_TPM_PWM + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/imx9_tpm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * 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(int pwmid); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_IMX9_TPM_PWM */ +#endif /* __ARCH_ARM64_SRC_IMX9_IMX9_TPM_PWM_H */