/****************************************************************************** * The MIT License * * Copyright (c) 2011, 2012 LeafLabs, LLC. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *****************************************************************************/ /** * @file libmaple/timer_private.h * @author Marti Bolivar * @brief Private, internal timer APIs. */ #ifndef _LIBMAPLE_TIMER_PRIVATE_H_ #define _LIBMAPLE_TIMER_PRIVATE_H_ /* * Helper macros for declaring timer_devs of various timer_types */ /* The indexes of user handlers in a timer_dev.handlers are just like * the corresponding DIER bits, as follows: */ /* Advanced timers: * [0] = Update handler; * [1,2,3,4] = capture/compare 1,2,3,4 handlers, respectively; * [5] = COM; * [6] = TRG; * [7] = BRK. */ #define NR_ADV_HANDLERS 8 /* General purpose timers: * [0] = update; * [1,2,3,4] = capture/compare 1,2,3,4; * [5] = ; * [6] = trigger. */ #define NR_GEN_HANDLERS 7 /* Basic timers: * [0] = update. */ #define NR_BAS_HANDLERS 1 /* For declaring advanced timers. */ #define ADVANCED_TIMER(num) \ { \ .regs = { .adv = TIMER##num##_BASE }, \ .clk_id = RCC_TIMER##num, \ .type = TIMER_ADVANCED, \ .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, \ } /* For declaring full-featured general purpose timers. */ #define GENERAL_TIMER(num) \ { \ .regs = { .gen = TIMER##num##_BASE }, \ .clk_id = RCC_TIMER##num, \ .type = TIMER_GENERAL, \ .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, \ } /* For declaring general purpose timers with limited interrupt * capability (e.g. timers 9 through 14 on STM32F2 and XL-density * STM32F1). */ #define RESTRICTED_GENERAL_TIMER(num, max_dier_bit) \ { \ .regs = { .gen = TIMER##num##_BASE }, \ .clk_id = RCC_TIMER##num, \ .type = TIMER_GENERAL, \ .handlers = { [max_dier_bit] = 0 }, \ } /* For declaring basic timers (e.g. TIM6 and TIM7). */ #define BASIC_TIMER(num) \ { \ .regs = { .bas = TIMER##num##_BASE }, \ .clk_id = RCC_TIMER##num, \ .type = TIMER_BASIC, \ .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, \ } /* * IRQ handlers * * These decode TIMx_DIER and TIMx_SR, then dispatch to the user-level * IRQ handlers. They also clean up TIMx_SR afterwards, so the user * doesn't have to deal with register details. * * Notes: * * - These dispatch routines make use of the fact that DIER interrupt * enable bits and SR interrupt flags have common bit positions. * Thus, ANDing DIER and SR lets us check if an interrupt is enabled * and if it has occurred simultaneously. * * - We force these routines to inline to avoid call overhead, but * there aren't any measurements to prove that this is actually a * good idea. Profile-directed optimizations are definitely wanted. */ /* A special-case dispatch routine for timers which only serve a * single interrupt on a given IRQ line. * * This function still checks DIER & SR, as in some cases, a timer may * only serve a single interrupt on a particular NVIC line, but that * line may be shared with another timer. For example, the timer 1 * update interrupt shares an IRQ line with the timer 10 interrupt on * STM32F1 (XL-density), STM32F2, and STM32F4. */ static __always_inline void dispatch_single_irq(timer_dev *dev, timer_interrupt_id iid, uint32 irq_mask) { timer_bas_reg_map *regs = (dev->regs).bas; if (regs->DIER & regs->SR & irq_mask) { void (*handler)(void) = dev->handlers[iid]; if (handler) { handler(); regs->SR &= ~irq_mask; } } } /* Helper macro for dispatch routines which service multiple interrupts. */ #define handle_irq(dier_sr, irq_mask, handlers, iid, handled_irq) do { \ if ((dier_sr) & (irq_mask)) { \ void (*__handler)(void) = (handlers)[iid]; \ if (__handler) { \ __handler(); \ handled_irq |= (irq_mask); \ } \ } \ } while (0) static __always_inline void dispatch_adv_brk(timer_dev *dev) { dispatch_single_irq(dev, TIMER_BREAK_INTERRUPT, TIMER_SR_BIF); } static __always_inline void dispatch_adv_up(timer_dev *dev) { dispatch_single_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF); } static __always_inline void dispatch_adv_trg_com(timer_dev *dev) { timer_adv_reg_map *regs = (dev->regs).adv; uint32 dsr = regs->DIER & regs->SR; void (**hs)(void) = dev->handlers; uint32 handled = 0; /* Logical OR of SR interrupt flags we end up * handling. We clear these. User handlers * must clear overcapture flags, to avoid * wasting time in output mode. */ handle_irq(dsr, TIMER_SR_TIF, hs, TIMER_TRG_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_COMIF, hs, TIMER_COM_INTERRUPT, handled); regs->SR &= ~handled; } static __always_inline void dispatch_adv_cc(timer_dev *dev) { timer_adv_reg_map *regs = (dev->regs).adv; uint32 dsr = regs->DIER & regs->SR; void (**hs)(void) = dev->handlers; uint32 handled = 0; handle_irq(dsr, TIMER_SR_CC4IF, hs, TIMER_CC4_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC3IF, hs, TIMER_CC3_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC2IF, hs, TIMER_CC2_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC1IF, hs, TIMER_CC1_INTERRUPT, handled); regs->SR &= ~handled; } static __always_inline void dispatch_general(timer_dev *dev) { timer_gen_reg_map *regs = (dev->regs).gen; uint32 dsr = regs->DIER & regs->SR; void (**hs)(void) = dev->handlers; uint32 handled = 0; handle_irq(dsr, TIMER_SR_TIF, hs, TIMER_TRG_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC4IF, hs, TIMER_CC4_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC3IF, hs, TIMER_CC3_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC2IF, hs, TIMER_CC2_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC1IF, hs, TIMER_CC1_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_UIF, hs, TIMER_UPDATE_INTERRUPT, handled); regs->SR &= ~handled; } /* On F1 (XL-density), F2, and F4, TIM9 and TIM12 are restricted * general-purpose timers with update, CC1, CC2, and TRG interrupts. */ static __always_inline void dispatch_tim_9_12(timer_dev *dev) { timer_gen_reg_map *regs = (dev->regs).gen; uint32 dsr = regs->DIER & regs->SR; void (**hs)(void) = dev->handlers; uint32 handled = 0; handle_irq(dsr, TIMER_SR_TIF, hs, TIMER_TRG_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC2IF, hs, TIMER_CC2_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_CC1IF, hs, TIMER_CC1_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_UIF, hs, TIMER_UPDATE_INTERRUPT, handled); regs->SR &= ~handled; } /* On F1 (XL-density), F2, and F4, timers 10, 11, 13, and 14 are * restricted general-purpose timers with update and CC1 interrupts. */ static __always_inline void dispatch_tim_10_11_13_14(timer_dev *dev) { timer_gen_reg_map *regs = (dev->regs).gen; uint32 dsr = regs->DIER & regs->SR; void (**hs)(void) = dev->handlers; uint32 handled = 0; handle_irq(dsr, TIMER_SR_CC1IF, hs, TIMER_CC1_INTERRUPT, handled); handle_irq(dsr, TIMER_SR_UIF, hs, TIMER_UPDATE_INTERRUPT, handled); regs->SR &= ~handled; } static __always_inline void dispatch_basic(timer_dev *dev) { dispatch_single_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF); } #endif