diff --git a/arch/arm/src/efm32/Kconfig b/arch/arm/src/efm32/Kconfig index 3d59d11deb..1f7f8f8528 100644 --- a/arch/arm/src/efm32/Kconfig +++ b/arch/arm/src/efm32/Kconfig @@ -177,6 +177,30 @@ config EFM32_OTGFS default n depends on EFM32_HAVE_OTGFS && EXPERIMENTAL +config EFM32_TIMER0 + bool "TIMER0" + default n + select ARCH_HAVE_TIMER0 + select EFM32_TIMER + +config EFM32_TIMER1 + bool "TIMER1" + default n + select ARCH_HAVE_TIMER1 + select EFM32_TIMER + +config EFM32_TIMER2 + bool "TIMER2" + default n + select ARCH_HAVE_TIMER2 + select EFM32_TIMER + +config EFM32_TIMER3 + bool "TIMER3" + default n + select ARCH_HAVE_TIMER3 + select EFM32_TIMER + endmenu # EFM32 Peripheral Support config EFM32_GPIO_IRQ @@ -430,4 +454,116 @@ config EFM32_OTGFS_SOFINTR endmenu +config EFM32_TIMER0 + bool "TIMER0" + default n + depends on EFM32_HAVE_TIMER0 + +config EFM32_TIMER0_PWM + bool "TIMER0 PWM" + default n + depends on EFM32_TIMER0 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 0 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. + If EFM32_TIMER0 is defined then THIS following may also be defined + to indicate that the timer is intended to be used for pulsed output + modulation. + +config EFM32_TIMER0_CHANNEL + int "TIMER0 PWM Output Channel" + default 0 + range 0 2 + depends on EFM32_TIMER0_PWM + ---help--- + If TIMER0 is enabled for PWM usage, you also need specifies the timer output + channel {0,1,2} + +config EFM32_TIMER1 + bool "TIMER1" + default n + depends on EFM32_HAVE_TIMER1 + +config EFM32_TIMER1_PWM + bool "TIMER1 PWM" + default n + depends on EFM32_TIMER1 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 0 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. + If EFM32_TIMER1 is defined then THIS following may also be defined + to indicate that the timer is intended to be used for pulsed output + modulation. + +config EFM32_TIMER1_CHANNEL + int "TIMER1 PWM Output Channel" + default 0 + range 0 2 + depends on EFM32_TIMER1_PWM + ---help--- + If TIMER1 is enabled for PWM usage, you also need specifies the timer output + channel {0,1,2} + +config EFM32_TIMER2 + bool "TIMER2" + default n + depends on EFM32_HAVE_TIMER2 + +config EFM32_TIMER2_PWM + bool "TIMER2 PWM" + default n + depends on EFM32_TIMER2 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 0 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. + If EFM32_TIMER2 is defined then THIS following may also be defined + to indicate that the timer is intended to be used for pulsed output + modulation. + +config EFM32_TIMER2_CHANNEL + int "TIMER2 PWM Output Channel" + default 0 + range 0 2 + depends on EFM32_TIMER2_PWM + ---help--- + If TIMER2 is enabled for PWM usage, you also need specifies the timer output + channel {0,1,2} + +config EFM32_TIMER3 + bool "TIMER3" + default n + depends on EFM32_HAVE_TIMER3 + +config EFM32_TIMER3_PWM + bool "TIMER3 PWM" + default n + depends on EFM32_TIMER3 + select ARCH_HAVE_PWM_PULSECOUNT + ---help--- + Reserve timer 0 for use by PWM + + Timer devices may be used for different purposes. One special purpose is + to generate modulated outputs for such things as motor control. + If EFM32_TIMER3 is defined then THIS following may also be defined + to indicate that the timer is intended to be used for pulsed output + modulation. + +config EFM32_TIMER3_CHANNEL + int "TIMER3 PWM Output Channel" + default 0 + range 0 2 + depends on EFM32_TIMER3_PWM + ---help--- + If TIMER3 is enabled for PWM usage, you also need specifies the timer output + channel {0,1,2} + endif # ARCH_CHIP_EFM32 diff --git a/arch/arm/src/efm32/Make.defs b/arch/arm/src/efm32/Make.defs index 3c19b698f6..6df8b4eddf 100644 --- a/arch/arm/src/efm32/Make.defs +++ b/arch/arm/src/efm32/Make.defs @@ -97,7 +97,7 @@ CHIP_ASRCS += efm32_vectors.S endif CHIP_CSRCS = efm32_start.c efm32_clockconfig.c efm32_irq.c efm32_timerisr.c -CHIP_CSRCS += efm32_gpio.c efm32_lowputc.c +CHIP_CSRCS += efm32_gpio.c efm32_lowputc.c efm32_timer.c ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += efm32_idle.c @@ -135,3 +135,7 @@ ifeq ($(CONFIG_USBHOST),y) CHIP_CSRCS += efm32_usbhost.c endif endif + +ifeq ($(CONFIG_PWM),y) +CHIP_CSRCS += efm32_pwm.c +endif diff --git a/arch/arm/src/efm32/efm32_pwm.c b/arch/arm/src/efm32/efm32_pwm.c new file mode 100644 index 0000000000..20c2d44f5a --- /dev/null +++ b/arch/arm/src/efm32/efm32_pwm.c @@ -0,0 +1,943 @@ +/**************************************************************************** + * arch/arm/src/efm32/efm32_pwm.c + * + * Copyright (C) 2014 Pierre-Noel Bouteville. All rights reserved. + * Author: Pierre-Noel Bouteville + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "up_arch.h" +#include "up_internal.h" + +#include "chip/efm32_cmu.h" +#include "chip/efm32_timer.h" +#include "efm32_timer.h" +#include "efm32_config.h" +#include "efm32_gpio.h" + +/* This module then only compiles if there is at least one enabled timer + * intended for use with the PWM upper half driver. + */ + +#if defined(CONFIG_EFM32_TIMER0_PWM) || \ + defined(CONFIG_EFM32_TIMER1_PWM) || \ + defined(CONFIG_EFM32_TIMER2_PWM) || \ + defined(CONFIG_EFM32_TIMER3_PWM) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* PWM/Timer Definitions ****************************************************/ +/* The following definitions are used to identify the various time types */ + +/* Debug ********************************************************************/ +/* Non-standard debug that may be enabled just for testing PWM */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_PWM +#endif + +#ifdef CONFIG_DEBUG_PWM +# define pwmdbg dbg +# define pwmlldbg lldbg +# ifdef CONFIG_DEBUG_VERBOSE +# define pwmvdbg vdbg +# define pwmllvdbg llvdbg +# define pwm_dumpgpio(p,m) efm32_dumpgpio(p,m) +# else +# define pwmlldbg(x...) +# define pwmllvdbg(x...) +# define pwm_dumpgpio(p,m) +# endif +#else +# define pwmdbg(x...) +# define pwmlldbg(x...) +# define pwmvdbg(x...) +# define pwmllvdbg(x...) +# define pwm_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This structure represents the state of one PWM timer */ + +struct efm32_pwmtimer_s +{ + FAR const struct pwm_ops_s *ops; /* PWM operations */ + uint8_t timid; /* Timer ID {1,...,14} */ + uint8_t channel; /* Timer output channel: {1,..4} */ + uint8_t pinloc; /* Timer output channel pin location */ +#ifdef CONFIG_PWM_PULSECOUNT + uint8_t irq; /* Timer update IRQ */ + uint8_t prev; /* The previous value of the RCR (pre-loaded) */ + uint8_t curr; /* The current value of the RCR (pre-loaded) */ + uint32_t count; /* Remaining pulse count */ +#endif + uint32_t base; /* The base address of the timer */ + uint32_t pincfg; /* Output pin configuration */ + uint32_t pclk; /* The frequency of the peripheral clock + * that drives the timer module. */ +#ifdef CONFIG_PWM_PULSECOUNT + FAR void *handle; /* Handle used for upper-half callback */ +#endif +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ +/* Register access */ + +static uint32_t pwm_getreg(struct efm32_pwmtimer_s *priv, int offset); +static void pwm_putreg(struct efm32_pwmtimer_s *priv, int offset, + uint32_t value); + +#if defined(CONFIG_DEBUG_PWM) && defined(CONFIG_DEBUG_VERBOSE) +static void pwm_dumpregs(struct efm32_pwmtimer_s *priv, FAR const char *msg); +#else +# define pwm_dumpregs(priv,msg) +#endif + +/* Timer management */ + +static int pwm_timer(FAR struct efm32_pwmtimer_s *priv, + FAR const struct pwm_info_s *info); + +#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_EFM32_TIMER0_PWM) || \ + defined(CONFIG_EFM32_TIMER1_PWM) || \ + defined(CONFIG_EFM32_TIMER2_PWM) || \ + defined(CONFIG_EFM32_TIMER3_PWM) \ + ) +static int pwm_interrupt(struct efm32_pwmtimer_s *priv); +#if defined(CONFIG_EFM32_TIMER0_PWM) +static int pwm_timer0_interrupt(int irq, void *context); +#endif +#if defined(CONFIG_EFM32_TIMER1_PWM) +static int pwm_timer1_interrupt(int irq, void *context); +#endif +#if defined(CONFIG_EFM32_TIMER2_PWM) +static int pwm_timer2_interrupt(int irq, void *context); +#endif +#if defined(CONFIG_EFM32_TIMER3_PWM) +static int pwm_timer3_interrupt(int irq, void *context); +#endif +static uint8_t pwm_pulsecount(uint32_t count); + +#endif + +/* PWM driver methods */ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(FAR struct pwm_lowerhalf_s *dev); + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, + FAR void *handle); +#else +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info); +#endif + +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); + +/**************************************************************************** + * 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, +}; + +#ifdef CONFIG_EFM32_TIMER0_PWM +static struct efm32_pwmtimer_s g_pwm0dev = +{ + .ops = &g_pwmops, + .timid = 0, + .channel = CONFIG_EFM32_TIMER0_CHANNEL, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = EFM32_IRQ_TIMER0, +#endif + .base = EFM32_TIMER0_BASE, + .pincfg = BOARD_PWM_TIMER0_PINCFG, + .pinloc = BOARD_PWM_TIMER0_PINLOC, + .pclk = BOARD_PWM_TIMER0_CLKIN, +}; +#endif + +#ifdef CONFIG_EFM32_TIMER1_PWM +static struct efm32_pwmtimer_s g_pwm1dev = +{ + .ops = &g_pwmops, + .timid = 0, + .channel = CONFIG_EFM32_TIMER1_CHANNEL, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = EFM32_IRQ_TIMER1, +#endif + .base = EFM32_TIMER1_BASE, + .pincfg = BOARD_PWM_TIMER1_PINCFG, + .pinloc = BOARD_PWM_TIMER1_PINLOC, + .pclk = BOARD_PWM_TIMER1_CLKIN, +}; +#endif + +#ifdef CONFIG_EFM32_TIMER2_PWM +static struct efm32_pwmtimer_s g_pwm2dev = +{ + .ops = &g_pwmops, + .timid = 0, + .channel = CONFIG_EFM32_TIMER2_CHANNEL, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = EFM32_IRQ_TIMER2, +#endif + .base = EFM32_TIMER2_BASE, + .pincfg = BOARD_PWM_TIMER2_PINCFG, + .pinloc = BOARD_PWM_TIMER2_PINLOC, + .pclk = BOARD_PWM_TIMER2_CLKIN, +}; +#endif + +#ifdef CONFIG_EFM32_TIMER3_PWM +static struct efm32_pwmtimer_s g_pwm3dev = +{ + .ops = &g_pwmops, + .timid = 0, + .channel = CONFIG_EFM32_TIMER3_CHANNEL, +#ifdef CONFIG_PWM_PULSECOUNT + .irq = EFM32_IRQ_TIMER3, +#endif + .base = EFM32_TIMER3_BASE, + .pincfg = BOARD_PWM_TIMER3_PINCFG, + .pinloc = BOARD_PWM_TIMER3_PINLOC, + .pclk = BOARD_PWM_TIMER3_CLKIN, +}; +#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 uint32_t pwm_getreg(struct efm32_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 void pwm_putreg(struct efm32_pwmtimer_s *priv, int offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: pwm_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input parameters: + * priv - A reference to the PWM block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_DEBUG_PWM) && defined(CONFIG_DEBUG_VERBOSE) +static void pwm_dumpregs(struct efm32_pwmtimer_s *priv, FAR const char *msg) +{ + /* TODO debug pwm_dumpregs */ +#if 0 + pwmvdbg("%s:\n", msg); + pwmvdbg(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + pwm_getreg(priv, STM32_GTIM_CR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CR2_OFFSET), + pwm_getreg(priv, STM32_GTIM_SMCR_OFFSET), + pwm_getreg(priv, STM32_GTIM_DIER_OFFSET)); + pwmvdbg(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n", + pwm_getreg(priv, STM32_GTIM_SR_OFFSET), + pwm_getreg(priv, STM32_GTIM_EGR_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCMR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCMR2_OFFSET)); + pwmvdbg(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + pwm_getreg(priv, STM32_GTIM_CCER_OFFSET), + pwm_getreg(priv, STM32_GTIM_CNT_OFFSET), + pwm_getreg(priv, STM32_GTIM_PSC_OFFSET), + pwm_getreg(priv, STM32_GTIM_ARR_OFFSET)); + pwmvdbg(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + pwm_getreg(priv, STM32_GTIM_CCR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR2_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR3_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR4_OFFSET)); +#if defined(CONFIG_STM32_TIM1_PWM) || defined(CONFIG_STM32_TIM8_PWM) + if (priv->timtype == TIMTYPE_ADVANCED) + { + pwmvdbg(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + pwm_getreg(priv, STM32_ATIM_RCR_OFFSET), + pwm_getreg(priv, STM32_ATIM_BDTR_OFFSET), + pwm_getreg(priv, STM32_ATIM_DCR_OFFSET), + pwm_getreg(priv, STM32_ATIM_DMAR_OFFSET)); + } + else +#endif + { + pwmvdbg(" DCR: %04x DMAR: %04x\n", + pwm_getreg(priv, STM32_GTIM_DCR_OFFSET), + pwm_getreg(priv, STM32_GTIM_DMAR_OFFSET)); + } +#endif +} +#endif + +/**************************************************************************** + * Name: pwm_timer + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input parameters: + * priv - 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_timer(FAR struct efm32_pwmtimer_s *priv, + FAR const struct pwm_info_s *info) +{ + /* Register contents */ + + uint32_t regval; + uint32_t cc_offet = EFM32_TIMER_CC_OFFSET(priv->channel); + + DEBUGASSERT(priv != NULL && info != NULL); + +#ifdef CONFIG_PWM_PULSECOUNT + pwmvdbg("TIMER%d channel: %d frequency: %d duty: %08x count: %d\n", + priv->timid, priv->channel, info->frequency, + info->duty, info->count); +#else + pwmvdbg("TIMER%d channel: %d frequency: %d duty: %08x\n", + priv->timid, priv->channel, info->frequency, info->duty); +#endif + DEBUGASSERT(info->frequency > 0 && info->duty >= 0 && + info->duty < uitoub16(100)); + + efm32_timer_reset(priv->base); + +#ifdef CONFIG_PWM_PULSECOUNT +#error "Not implemented ! Sorry" +#endif + + if ( efm32_timer_set_freq(priv->base,priv->pclk,info->frequency) < 0 ) + { + pwmdbg("Cannot set TIMER frequency %dHz from clock %dHz\n", + info->frequency, + priv->pclk + ); + return -EINVAL; + } + + regval = ((uint32_t)(priv->pinloc)) << _TIMER_ROUTE_LOCATION_SHIFT; + + switch(priv->channel) + { + case 0: + regval |= _TIMER_ROUTE_CC0PEN_MASK; + break; + + case 1: + regval |= _TIMER_ROUTE_CC1PEN_MASK; + break; + + case 2: + regval |= _TIMER_ROUTE_CC2PEN_MASK; + break; + + default: + ASSERT(false); + } + + pwm_putreg( priv, EFM32_TIMER_ROUTE_OFFSET, regval ); + + regval = (info->duty * pwm_getreg(priv, EFM32_TIMER_TOP_OFFSET)) >> 16; + pwm_putreg(priv, cc_offet + EFM32_TIMER_CC_CCV_OFFSET , regval); + //pwm_putreg(priv, cc_offet + EFM32_TIMER_CC_CCVB_OFFSET, regval); + + regval = (_TIMER_CC_CTRL_MODE_PWM << _TIMER_CC_CTRL_MODE_SHIFT) | \ + (_TIMER_CC_CTRL_CMOA_CLEAR << _TIMER_CC_CTRL_CMOA_SHIFT) | \ + (_TIMER_CC_CTRL_COFOA_SET << _TIMER_CC_CTRL_COFOA_SHIFT) ; + + pwm_putreg(priv, cc_offet + EFM32_TIMER_CC_CTRL_OFFSET, regval ); + + /* Start Timer */ + + pwm_putreg(priv, EFM32_TIMER_CMD_OFFSET, TIMER_CMD_START ); + pwm_dumpregs(priv, "After starting"); + return OK; +} + +/**************************************************************************** + * Name: pwm_interrupt + * + * Description: + * Handle timer interrupts. + * + * Input parameters: + * priv - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_EFM32_TIMER0_PWM) || \ + defined(CONFIG_EFM32_TIMER1_PWM) || \ + defined(CONFIG_EFM32_TIMER2_PWM) || \ + defined(CONFIG_EFM32_TIMER3_PWM) \ + ) +#warning "not yet implemented" +static int pwm_interrupt(struct efm32_pwmtimer_s *priv) +{ + /* TODO pwm_interrupt */ +#if 0 + uint32_t regval; + + /* Verify that this is an update interrupt. Nothing else is expected. */ + + regval = pwm_getreg(priv, STM32_ATIM_SR_OFFSET); + DEBUGASSERT((regval & ATIM_SR_UIF) != 0); + + /* Clear the UIF interrupt bit */ + + pwm_putreg(priv, STM32_ATIM_SR_OFFSET, regval & ~ATIM_SR_UIF); + + /* Calculate the new count by subtracting the number of pulses + * since the last interrupt. + */ + + if (priv->count <= priv->prev) + { + /* We are finished. Turn off the mast output to stop the output as + * quickly as possible. + */ + + regval = pwm_getreg(priv, STM32_ATIM_BDTR_OFFSET); + regval &= ~ATIM_BDTR_MOE; + pwm_putreg(priv, STM32_ATIM_BDTR_OFFSET, regval); + + /* Disable first interrupts, stop and reset the timer */ + + (void)pwm_stop((FAR struct pwm_lowerhalf_s *)priv); + + /* Then perform the callback into the upper half driver */ + + pwm_expired(priv->handle); + + priv->handle = NULL; + priv->count = 0; + priv->prev = 0; + priv->curr = 0; + } + else + { + /* Decrement the count of pulses remaining using the number of + * pulses generated since the last interrupt. + */ + + priv->count -= priv->prev; + + /* Set up the next RCR. Set 'prev' to the value of the RCR that + * was loaded when the update occurred (just before this interrupt) + * and set 'curr' to the current value of the RCR register (which + * will bet loaded on the next update event). + */ + + priv->prev = priv->curr; + priv->curr = pwm_pulsecount(priv->count - priv->prev); + pwm_putreg(priv, STM32_ATIM_RCR_OFFSET, (uint32_t)priv->curr - 1); + } + + /* Now all of the time critical stuff is done so we can do some debug output */ + + pwmllvdbg("Update interrupt SR: %04x prev: %d curr: %d count: %d\n", + regval, priv->prev, priv->curr, priv->count); + + return OK; +#else + return -ENODEV; +#endif +} +#endif + +/**************************************************************************** + * Name: pwm_timer1/3_interrupt + * + * Description: + * Handle timer 1..3 interrupts. + * + * Input parameters: + * Standard NuttX interrupt inputs + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_EFM32_TIMER0_PWM) +static int pwm_timer0_interrupt(int irq, void *context) +{ + return pwm_interrupt(&g_pwm0dev); +} +#endif + +#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_EFM32_TIMER1_PWM) +static int pwm_timer1_interrupt(int irq, void *context) +{ + return pwm_interrupt(&g_pwm1dev); +} +#endif + +#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_EFM32_TIMER2_PWM) +static int pwm_timer2_interrupt(int irq, void *context) +{ + return pwm_interrupt(&g_pwm2dev); +} +#endif + +#if defined(CONFIG_PWM_PULSECOUNT) && defined(CONFIG_EFM32_TIMER3_PWM) +static int pwm_timer3_interrupt(int irq, void *context) +{ + return pwm_interrupt(&g_pwm3dev); +} +#endif + +/**************************************************************************** + * Name: pwm_pulsecount + * + * Description: + * Pick an optimal pulse count to program the RCR. + * + * Input parameters: + * count - The total count remaining + * + * Returned Value: + * The recommended pulse count + * + ****************************************************************************/ + +#if defined(CONFIG_PWM_PULSECOUNT) && (defined(CONFIG_EFM32_TIMER0_PWM) || \ + defined(CONFIG_EFM32_TIMER1_PWM) || \ + defined(CONFIG_EFM32_TIMER2_PWM) || \ + defined(CONFIG_EFM32_TIMER3_PWM) \ + ) +static uint8_t pwm_pulsecount(uint32_t count) +{ + /* The the remaining pulse count is less than or equal to the maximum, the + * just return the count. + */ + + if (count <= ATIM_RCR_REP_MAX) + { + return count; + } + + /* Otherwise, we have to be careful. We do not want a small number of + * counts at the end because we might have trouble responding fast enough. + * If the remaining count is less than 150% of the maximum, then return + * half of the maximum. In this case the final sequence will be between 64 + * and 128. + */ + + else if (count < (3 * ATIM_RCR_REP_MAX / 2)) + { + return (ATIM_RCR_REP_MAX + 1) >> 1; + } + + /* Otherwise, return the maximum. The final count will be 64 or more */ + + else + { + return ATIM_RCR_REP_MAX; + } +} +#endif + + +/**************************************************************************** + * 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: + * APB1 or 2 clocking for the GPIOs has already been configured by the RCC + * logic at power up. + * + ****************************************************************************/ + +static int pwm_setup(FAR struct pwm_lowerhalf_s *dev) +{ + FAR struct efm32_pwmtimer_s *priv = (FAR struct efm32_pwmtimer_s *)dev; + + pwmvdbg("TIMER%d pincfg: %08x\n", priv->timid, priv->pincfg); + pwm_dumpregs(priv, "Initially"); + + /* Configure the PWM output pin, but do not start the timer yet */ + + /* Dnable TIMER clock */ + + switch(priv->timid) + { + case 0: + modifyreg32(EFM32_CMU_HFPERCLKEN0,0,CMU_HFPERCLKEN0_TIMER0); + break; + + case 1: + modifyreg32(EFM32_CMU_HFPERCLKEN0,0,CMU_HFPERCLKEN0_TIMER1); + break; + + case 2: + modifyreg32(EFM32_CMU_HFPERCLKEN0,0,CMU_HFPERCLKEN0_TIMER2); + break; + + case 3: + modifyreg32(EFM32_CMU_HFPERCLKEN0,0,CMU_HFPERCLKEN0_TIMER3); + break; + + default: + ASSERT(false); + break; + } + + efm32_configgpio(priv->pincfg); + pwm_putreg(priv,EFM32_TIMER_ROUTE_OFFSET,BOARD_PWM_TIMER0_PINLOC); + pwm_dumpgpio(priv->pincfg, "PWM setup"); + 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 efm32_pwmtimer_s *priv = (FAR struct efm32_pwmtimer_s *)dev; + uint32_t pincfg; + + pwmvdbg("TIMER%d pincfg: %08x\n", priv->timid, priv->pincfg); + + /* Make sure that the output has been stopped */ + + pwm_stop(dev); + + /* Then put the GPIO pin back to the default state */ + + pincfg = priv->pincfg & (GPIO_PORT_MASK|GPIO_PIN_MASK); + + pincfg |= (_GPIO_DISABLE); + + efm32_configgpio(pincfg); + 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 + * + ****************************************************************************/ + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info, + FAR void *handle) +{ + FAR struct efm32_pwmtimer_s *priv = (FAR struct efm32_pwmtimer_s *)dev; + + /* Save the handle */ + + priv->handle = handle; + + /* Start the time */ + + return pwm_timer(priv, info); +} +#else +static int pwm_start(FAR struct pwm_lowerhalf_s *dev, + FAR const struct pwm_info_s *info) +{ + FAR struct efm32_pwmtimer_s *priv = (FAR struct efm32_pwmtimer_s *)dev; + return pwm_timer(priv, info); +} +#endif + +/**************************************************************************** + * 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 efm32_pwmtimer_s *priv = (FAR struct efm32_pwmtimer_s *)dev; + irqstate_t flags; + + pwmvdbg("TIMER%d\n", priv->timid); + + /* Disable interrupts momentary to stop any ongoing timer processing and + * to prevent any concurrent access to the reset register. + */ + + flags = irqsave(); + + /* Reset the timer - stopping the output and putting the timer back + * into a state where pwm_start() can be called. + */ + + pwm_putreg(priv, EFM32_TIMER_CMD_OFFSET, TIMER_CMD_STOP); + + irqrestore( flags); + + pwm_dumpregs(priv, "After stop"); + 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) +{ +#ifdef CONFIG_DEBUG_PWM + FAR struct efm32_pwmtimer_s *priv = (FAR struct efm32_pwmtimer_s *)dev; + + /* There are no platform-specific ioctl commands */ + + pwmvdbg("TIMER%d\n", priv->timid); +#endif + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: efm32_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the STM32 MCU and MCU family but is somewhere in + * the range of {1,..,14}. + * + * Returned Value: + * On success, a pointer to the STM32 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct pwm_lowerhalf_s *efm32_pwminitialize(int timer) +{ + FAR struct efm32_pwmtimer_s *lower; + + pwmvdbg("TIMER%d\n", timer); + + switch (timer) + { +#ifdef CONFIG_EFM32_TIMER0_PWM + case 0: + lower = &g_pwm0dev; + + /* Attach but disable the TIM1 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_timer0_interrupt); + up_disable_irq(lower->irq); +#endif + break; +#endif + +#ifdef CONFIG_EFM32_TIMER1_PWM + case 1: + lower = &g_pwm1dev; + + /* Attach but disable the TIM1 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_timer0_interrupt); + up_disable_irq(lower->irq); +#endif + break; +#endif +#ifdef CONFIG_EFM32_TIMER2_PWM + case 2: + lower = &g_pwm2dev; + + /* Attach but disable the TIM1 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_timer2_interrupt); + up_disable_irq(lower->irq); +#endif + break; +#endif +#ifdef CONFIG_EFM32_TIMER3_PWM + case 3: + lower = &g_pwm3dev; + + /* Attach but disable the TIM1 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_timer3_interrupt); + up_disable_irq(lower->irq); +#endif + break; +#endif + + default: + pwmdbg("No such timer configured\n"); + return NULL; + } + + return (FAR struct pwm_lowerhalf_s *)lower; +} + +#endif /* CONFIG_EFM32_TIMn_PWM, n = 0,..,3 */ diff --git a/arch/arm/src/efm32/efm32_pwm.h b/arch/arm/src/efm32/efm32_pwm.h new file mode 100644 index 0000000000..23470f7c20 --- /dev/null +++ b/arch/arm/src/efm32/efm32_pwm.h @@ -0,0 +1,154 @@ +/****************************************************************************** + * arch/arm/src/efm32/efm32_pwm.h + * + * Copyright (C) 2014 Pierre-Noel Bouteville. All rights reserved. + * Author: Pierre-Noel Bouteville + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef __ARCH_ARM_SRC_EFM32_EFM32_PWM_H +#define __ARCH_ARM_SRC_EFM32_EFM32_PWM_H + +/* The EFM32 does not have dedicated PWM hardware. Rather, pulsed output + * control is a capability of the EFM32 timers. The logic in this file + * implements the lower half of the standard, NuttX PWM interface using the + * EFM32 timers. That interface is described in include/nuttx/pwm.h. + */ + +/****************************************************************************** + * Included Files + ******************************************************************************/ + +#include + +#include "chip.h" + +/****************************************************************************** + * Pre-processor Definitions + ******************************************************************************/ +/* Configuration **************************************************************/ +/* Timer devices may be used for different purposes. One special purpose is + * to generate modulated outputs for such things as motor control. + * If CONFIG_EFM32_TIMERn is defined then the CONFIG_EFM32_TIMERn_PWM must also + * be defined to indicate that timer "n" is intended to be used for pulsed + * output signal generation. + */ + +#ifndef CONFIG_EFM32_TIMER0 +# undef CONFIG_EFM32_TIMER0_PWM +#endif +#ifndef CONFIG_EFM32_TIMER1 +# undef CONFIG_EFM32_TIMER1_PWM +#endif +#ifndef CONFIG_EFM32_TIMER2 +# undef CONFIG_EFM32_TIMER2_PWM +#endif +#ifndef CONFIG_EFM32_TIMER3 +# undef CONFIG_EFM32_TIMER3_PWM +#endif + +/* Check if PWM support for any channel is enabled. */ + +#if defined(CONFIG_EFM32_TIMER0_PWM) || \ + defined(CONFIG_EFM32_TIMER1_PWM) || \ + defined(CONFIG_EFM32_TIMER2_PWM) || \ + defined(CONFIG_EFM32_TIMER3_PWM) + +#include +#include "chip/efm32_timer.h" + +/* For each timer that is enabled for PWM usage, we need the following additional + * configuration settings: + * + * CONFIG_EFM32_TIMERx_CHANNEL - Specifies the timer output channel {0,1,3} + * BOARD_PWM_TIMERx_PINCFG - Specifies the timer output pin configuration. + * example : (GPIO_PORTC|GPIO_PIN0|GPIO_OUTPUT_PUSHPULL) + * + * BOARD_PWM_TIMERx_PINLOC - Specifies the timer output pin location. + * example : _TIMER_ROUTE_LOCATION_LOC4 + * + * BOARD_PWM_TIMERx_CLKIN - Specifies the timer input clock frequency (in Hz). + * example : 48e6 for 48MHz + * + * NOTE: The EFM32 timers are each capable of generating different signals on + * each of the four channels with different duty cycles. That capability is + * not supported by this driver: Only one output channel per timer. + */ + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: efm32_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the EFM32 MCU and MCU family but is somewhere in + * the range of {0,..,3}. + * + * Returned Value: + * On success, a pointer to the EFM32 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ************************************************************************************/ + +FAR struct pwm_lowerhalf_s *efm32_pwminitialize(int timer); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_EFM32_TIMERx_PWM */ +#endif /* __ARCH_ARM_SRC_EFM32_EFM32_PWM_H */ diff --git a/arch/arm/src/efm32/efm32_serial.c b/arch/arm/src/efm32/efm32_serial.c index 86229d3b4a..139de55a2a 100644 --- a/arch/arm/src/efm32/efm32_serial.c +++ b/arch/arm/src/efm32/efm32_serial.c @@ -64,6 +64,7 @@ #include "chip/efm32_usart.h" #include "efm32_config.h" +#include "efm32_gpio.h" #include "efm32_lowputc.h" /**************************************************************************** diff --git a/arch/arm/src/efm32/efm32_timer.c b/arch/arm/src/efm32/efm32_timer.c new file mode 100644 index 0000000000..407a2a8397 --- /dev/null +++ b/arch/arm/src/efm32/efm32_timer.c @@ -0,0 +1,273 @@ +/**************************************************************************** + * arch/arm/src/efm32/efm32_timer.c + * + * Copyright (C) 2014 Pierre-Noel Bouteville. All rights reserved. + * Author: Pierre-Noel Bouteville + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "up_arch.h" +#include "up_internal.h" + +#include "chip/efm32_timer.h" +#include "efm32_config.h" +#include "efm32_gpio.h" + + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Debug ********************************************************************/ +/* Non-standard debug that may be enabled just for testing TIMER */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_TIMER +#endif + +#ifdef CONFIG_DEBUG_TIMER +# define efm32_timerdbg dbg +# define efm32_timerlldbg lldbg +# ifdef CONFIG_DEBUG_VERBOSE +# define efm32_timervdbg vdbg +# define efm32_timerllvdbg llvdbg +# define efm32_timer_dumpgpio(p,m) efm32_dumpgpio(p,m) +# else +# define efm32_timerlldbg(x...) +# define efm32_timerllvdbg(x...) +# define efm32_timer_dumpgpio(p,m) +# endif +#else +# define efm32_timerdbg(x...) +# define efm32_timerlldbg(x...) +# define efm32_timervdbg(x...) +# define efm32_timerllvdbg(x...) +# define efm32_timer_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: efm32_timer_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input parameters: + * base - A base address of timer + * + * Returned Value: + * None + * + ****************************************************************************/ + +void efm32_timer_dumpregs(uintptr_t base, FAR const char *msg) +{ + int i; + + efm32_timervdbg("%s:\n", msg); + efm32_timervdbg(" CTRL: %04x STATUS: %04x IEN: %04x IF: %04x\n", + getreg32(base + EFM32_TIMER_CTRL_OFFSET ), + getreg32(base + EFM32_TIMER_STATUS_OFFSET ), + getreg32(base + EFM32_TIMER_IEN_OFFSET ), + getreg32(base + EFM32_TIMER_IF_OFFSET ) + ); + efm32_timervdbg(" TOP: %04x TOPB: %04x CNT: %04x ROUTE: %04x\n", + getreg32(base + EFM32_TIMER_TOP_OFFSET ), + getreg32(base + EFM32_TIMER_TOPB_OFFSET ), + getreg32(base + EFM32_TIMER_CNT_OFFSET ), + getreg32(base + EFM32_TIMER_ROUTE_OFFSET ) + ); + + for (i = 0; i < EFM32_TIMER_NCC; i++) + { +#if defined(CONFIG_DEBUG_TIMER) && defined(CONFIG_DEBUG_VERBOSE) + uintptr_t base_cc = base + EFM32_TIMER_CC_OFFSET(i); +#endif + efm32_timervdbg("CC%d => CTRL: %04x CCV: %04x CCVP: %04x CCVB: %04x\n", + i + getreg32(base_cc + EFM32_TIMER_CC_CTRL_OFFSET ), + getreg32(base_cc + EFM32_TIMER_CC_CCV_OFFSET ), + getreg32(base_cc + EFM32_TIMER_CC_CCVP_OFFSET ), + getreg32(base_cc + EFM32_TIMER_CC_CCVB_OFFSET ) + ); + } + + efm32_timervdbg("DTCTRL: %04x DTTIME: %04x DTFC: %04x DTOGEN: %04x\n", + getreg32(base + EFM32_TIMER_CTRL_OFFSET ), + getreg32(base + EFM32_TIMER_STATUS_OFFSET ), + getreg32(base + EFM32_TIMER_IEN_OFFSET ), + getreg32(base + EFM32_TIMER_IF_OFFSET ) + ); + efm32_timervdbg("DTFAULT: %04x DTFAULTC: %04x DTLOCK: %04x \n", + getreg32(base + EFM32_TIMER_CTRL_OFFSET ), + getreg32(base + EFM32_TIMER_STATUS_OFFSET ), + getreg32(base + EFM32_TIMER_IEN_OFFSET ), + getreg32(base + EFM32_TIMER_IF_OFFSET ) + ); +} + +/**************************************************************************** + * Name: efm32_timer_reset + * + * Description: + * reset timer into reset state + * + * Input parameters: + * base - A base address of timer + * + * Returned Value: + * None + * + ****************************************************************************/ + +void efm32_timer_reset(uintptr_t base) +{ + int i; + + /* Make sure disabled first, before resetting other registers */ + + putreg32(TIMER_CMD_STOP, base + EFM32_TIMER_CMD_OFFSET); + + /* Reset timer register */ + + putreg32(_TIMER_CTRL_RESETVALUE, base + EFM32_TIMER_CTRL_OFFSET ); + putreg32(_TIMER_IEN_RESETVALUE, base + EFM32_TIMER_STATUS_OFFSET ); + putreg32(_TIMER_IFC_MASK, base + EFM32_TIMER_IEN_OFFSET ); + putreg32(_TIMER_TOP_RESETVALUE, base + EFM32_TIMER_IF_OFFSET ); + putreg32(_TIMER_TOPB_RESETVALUE, base + EFM32_TIMER_CTRL_OFFSET ); + putreg32(_TIMER_CNT_RESETVALUE, base + EFM32_TIMER_CMD_OFFSET ); + + /* Do not reset route register, setting should be done independently */ + /* (Note: ROUTE register may be locked by DTLOCK register.) */ + //putreg32(_TIMER_ROUTE_RESETVALUE, base + EFM32_TIMER_ROUTE_OFFSET ); + + for(i = 0; i < EFM32_TIMER_NCC; i++) + { + uintptr_t base_cc = base + EFM32_TIMER_CC_OFFSET(i); + putreg32(_TIMER_CC_CTRL_RESETVALUE, base_cc+EFM32_TIMER_CC_CTRL_OFFSET); + putreg32(_TIMER_CC_CCV_RESETVALUE, base_cc+EFM32_TIMER_CC_CCV_OFFSET ); + putreg32(_TIMER_CC_CCVB_RESETVALUE, base_cc+EFM32_TIMER_CC_CCVB_OFFSET); + } + + /* Reset dead time insertion module, no effect on timers without DTI */ + +#ifdef TIMER_DTLOCK_LOCKKEY_UNLOCK + /* Unlock DTI registers first in case locked */ + + putreg32(TIMER_DTLOCK_LOCKKEY_UNLOCK, base + EFM32_TIMER_DTLOCK_OFFSET); + + putreg32(_TIMER_DTCTRL_RESETVALUE,base + EFM32_TIMER_DTCTRL_OFFSET ); + putreg32(_TIMER_DTTIME_RESETVALUE,base + EFM32_TIMER_DTTIME_OFFSET ); + putreg32(_TIMER_DTFC_RESETVALUE, base + EFM32_TIMER_DTFC_OFFSET ); + putreg32(_TIMER_DTOGEN_RESETVALUE,base + EFM32_TIMER_DTOGEN_OFFSET ); + putreg32(_TIMER_DTFAULTC_MASK, base + EFM32_TIMER_DTFAULTC_OFFSET ); +#endif +} + +/**************************************************************************** + * Name: efm32_timer_set_freq + * + * Description: + * set prescaler and top timer with best value to have "freq" + * + * Input parameters: + * base - A base address of timer + * clk_freq - Clock soure of timer. + * freq - Wanted freqency. + * + * Returned Value: + * prescaler setted, -1 in case of error. + * + ****************************************************************************/ +int efm32_timer_set_freq(uintptr_t base, uint32_t clk_freq, uint32_t freq) +{ + int prescaler = 0; + int cnt_freq = clk_freq>>16; + int reload; + + while ( cnt_freq > freq ) + { + prescaler++; + cnt_freq>>=1; + if ( prescaler > (_TIMER_CTRL_PRESC_MASK>>_TIMER_CTRL_PRESC_SHIFT)) + { + return -1; + } + } + + modifyreg32(base + EFM32_TIMER_CTRL_OFFSET, + _TIMER_CTRL_PRESC_MASK, + prescaler<<_TIMER_CTRL_PRESC_SHIFT + ); + + prescaler = 1< + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_EFM32_EFM32_TIMER_H +#define __ARCH_ARM_SRC_EFM32_EFM32_TIMER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "chip/efm32_timer.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +void efm32_timer_reset(uintptr_t base); +void efm32_timer_dumpregs(uintptr_t base, FAR const char *msg); +int efm32_timer_set_freq(uintptr_t base, uint32_t clk_freq, uint32_t freq); + +#endif /* __ARCH_ARM_SRC_EFM32_EFM32_TIMER_H */ +