/* * Copyright (C) 2014 Loci Controls Inc. * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup cpu_cc2538 * @ingroup drivers_periph_timer * @{ * * @file * @brief Low-level timer driver implementation for the CC2538 CPU * * @author Ian Martin * * @} */ #include #include #include "board.h" #include "cpu.h" #include "periph/timer.h" #include "periph_conf.h" #define ENABLE_DEBUG (0) #include "debug.h" #define LOAD_VALUE (0xffff) #define TIMER_A_IRQ_MASK (0x000000ff) #define TIMER_B_IRQ_MASK (0x0000ff00) #define BIT(n) ( 1UL << (n) ) /* GPTIMER_CTL Bits: */ #define TBEN BIT(8) #define TAEN BIT(0) /* GPTIMER_TnMR Bits: */ #define TnCMIE BIT(5) #define TnCDIR BIT(4) /* GPTIMER_IMR Bits: */ #define TBMIM BIT(11) #define TAMIM BIT(4) typedef struct { uint16_t mask; uint16_t flag; } _isr_cfg_t; static const _isr_cfg_t chn_isr_cfg[] = { { .mask = TIMER_A_IRQ_MASK, .flag = TAMIM }, { .mask = TIMER_B_IRQ_MASK, .flag = TBMIM } }; static const int irqn_cfg[] = { GPTIMER_0A_IRQn, GPTIMER_1A_IRQn, GPTIMER_2A_IRQn, GPTIMER_3A_IRQn }; /** * @brief Timer state memory */ static timer_isr_ctx_t isr_ctx[TIMER_NUMOF]; /* enable timer interrupts */ static inline void _irq_enable(tim_t tim) { DEBUG("%s(%u)\n", __FUNCTION__, tim); if (tim < TIMER_NUMOF) { IRQn_Type irqn = irqn_cfg[tim]; NVIC_SetPriority(irqn, TIMER_IRQ_PRIO); NVIC_EnableIRQ(irqn); if (timer_config[tim].chn == 2) { irqn++; NVIC_SetPriority(irqn, TIMER_IRQ_PRIO); NVIC_EnableIRQ(irqn); } } } static inline cc2538_gptimer_t *dev(tim_t tim) { assert(tim < TIMER_NUMOF); return ((cc2538_gptimer_t *)(GPTIMER_BASE | (((uint32_t)tim) << 12))); } /** * @brief Setup the given timer * */ int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg) { DEBUG("%s(%u, %lu, %p, %p)\n", __FUNCTION__, tim, freq, cb, arg); if (tim >= TIMER_NUMOF) { return -1; } /* Save the callback function: */ assert(tim < TIMER_NUMOF); isr_ctx[tim].cb = cb; isr_ctx[tim].arg = arg; /* Enable the clock for this timer: */ SYS_CTRL_RCGCGPT |= (1 << tim); /* Disable this timer before configuring it: */ dev(tim)->cc2538_gptimer_ctl.CTL = 0; uint32_t prescaler = 0; uint32_t chan_mode = TnCMIE | GPTIMER_PERIODIC_MODE; if (timer_config[tim].cfg == GPTMCFG_32_BIT_TIMER) { /* Count up in periodic mode */ chan_mode |= TnCDIR ; if (timer_config[tim].chn > 1) { DEBUG("Invalid timer_config. Multiple channels are available only in 16-bit mode."); return -1; } if (freq != sys_clock_freq()) { DEBUG("In 32-bit mode, the GPTimer frequency must equal the system clock frequency (%u).", sys_clock_freq()); return -1; } } else if (timer_config[tim].cfg == GPTMCFG_16_BIT_TIMER) { prescaler = sys_clock_freq(); prescaler += freq / 2; prescaler /= freq; if (prescaler > 0) prescaler--; if (prescaler > 255) prescaler = 255; dev(tim)->TAPR = prescaler; dev(tim)->TAILR = LOAD_VALUE; } else { DEBUG("timer_init: invalid timer config must be 16 or 32Bit mode!\n"); return -1; } dev(tim)->CFG = timer_config[tim].cfg; dev(tim)->cc2538_gptimer_ctl.CTL = TAEN; dev(tim)->cc2538_gptimer_tamr.TAMR = chan_mode; if (timer_config[tim].chn > 1) { dev(tim)->cc2538_gptimer_tbmr.TBMR = chan_mode; dev(tim)->TBPR = prescaler; dev(tim)->TBILR = LOAD_VALUE; /* Enable the timer: */ dev(tim)->cc2538_gptimer_ctl.CTL = TBEN | TAEN; } /* Enable interrupts for given timer: */ _irq_enable(tim); return 0; } int timer_set_absolute(tim_t tim, int channel, unsigned int value) { DEBUG("%s(%u, %u, %u)\n", __FUNCTION__, tim, channel, value); if ((tim >= TIMER_NUMOF) || (channel >= timer_config[tim].chn) ) { return -1; } /* clear any pending match interrupts */ dev(tim)->ICR = chn_isr_cfg[channel].flag; if (channel == 0) { dev(tim)->TAMATCHR = (timer_config[tim].cfg == GPTMCFG_32_BIT_TIMER) ? value : (LOAD_VALUE - value); } else { dev(tim)->TBMATCHR = (LOAD_VALUE - value); } dev(tim)->cc2538_gptimer_imr.IMR |= chn_isr_cfg[channel].flag; return 1; } int timer_clear(tim_t tim, int channel) { DEBUG("%s(%u, %u)\n", __FUNCTION__, tim, channel); if ( (tim >= TIMER_NUMOF) || (channel >= timer_config[tim].chn) ) { return -1; } /* clear interupt flags */ dev(tim)->cc2538_gptimer_imr.IMR &= ~(chn_isr_cfg[channel].flag); return 1; } /* * The timer channels 1 and 2 are configured to run with the same speed and * have the same value (they run in parallel), so only on of them is returned. */ unsigned int timer_read(tim_t tim) { DEBUG("%s(%u)\n", __FUNCTION__, tim); if (tim >= TIMER_NUMOF) { return 0; } if (timer_config[tim].cfg == GPTMCFG_32_BIT_TIMER) { return dev(tim)->TAV; } else { return LOAD_VALUE - (dev(tim)->TAV & 0xffff); } } /* * For stopping the counting of all channels. */ void timer_stop(tim_t tim) { DEBUG("%s(%u)\n", __FUNCTION__, tim); if (tim < TIMER_NUMOF) { dev(tim)->cc2538_gptimer_ctl.CTL = 0; } } void timer_start(tim_t tim) { DEBUG("%s(%u)\n", __FUNCTION__, tim); if (tim < TIMER_NUMOF) { if (timer_config[tim].chn == 1) { dev(tim)->cc2538_gptimer_ctl.CTL = TAEN; } else if (timer_config[tim].chn == 2) { dev(tim)->cc2538_gptimer_ctl.CTL = TBEN | TAEN; } } } /** * @brief timer interrupt handler * * @param[in] num GPT instance number * @param[in] chn channel number (0=A, 1=B) */ static void irq_handler(tim_t tim, int channel) { DEBUG("%s(%u,%d)\n", __FUNCTION__, tim, channel); assert(tim < TIMER_NUMOF); assert(channel < timer_config[tim].chn); uint32_t mis; /* Latch the active interrupt flags */ mis = dev(tim)->MIS & chn_isr_cfg[channel].mask; /* Clear the latched interrupt flags */ dev(tim)->ICR = mis; if (mis & chn_isr_cfg[channel].flag) { /* Disable further match interrupts for this timer/channel */ dev(tim)->cc2538_gptimer_imr.IMR &= ~chn_isr_cfg[channel].flag; /* Invoke the callback function */ isr_ctx[tim].cb(isr_ctx[tim].arg, channel); } cortexm_isr_end(); } void isr_timer0_chan0(void) {irq_handler(0, 0);} void isr_timer0_chan1(void) {irq_handler(0, 1);} void isr_timer1_chan0(void) {irq_handler(1, 0);} void isr_timer1_chan1(void) {irq_handler(1, 1);} void isr_timer2_chan0(void) {irq_handler(2, 0);} void isr_timer2_chan1(void) {irq_handler(2, 1);} void isr_timer3_chan0(void) {irq_handler(3, 0);} void isr_timer3_chan1(void) {irq_handler(3, 1);}