This is an automated email from the ASF dual-hosted git repository. utzig pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-core.git
commit de4046031edfaf03a90b88ff0760141db27fc924 Author: Fabio Utzig <ut...@apache.org> AuthorDate: Thu Aug 19 12:05:06 2021 -0300 hw: mcu: kinetis: Add initial hal_timer support Implement initial hal_timer interface using the LPTMR modules in the Kinetis MCU line. Signed-off-by: Fabio Utzig <ut...@apache.org> --- hw/mcu/nxp/kinetis/src/hal_timer.c | 437 +++++++++++++++++++++++++++++++++++++ hw/mcu/nxp/kinetis/syscfg.yml | 7 + 2 files changed, 444 insertions(+) diff --git a/hw/mcu/nxp/kinetis/src/hal_timer.c b/hw/mcu/nxp/kinetis/src/hal_timer.c new file mode 100644 index 0000000..4fadd01 --- /dev/null +++ b/hw/mcu/nxp/kinetis/src/hal_timer.c @@ -0,0 +1,437 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include <assert.h> + +#include "os/mynewt.h" +#include "hal/hal_timer.h" +#include "mcu/cmsis_nvic.h" + +#include "fsl_lptmr.h" + +#define KINETIS_TIMERS_MAX 2 + +#if (FSL_FEATURE_LPTMR_CNR_WIDTH_IS_32B != 0) +#error "The Kinetis hal_timer driver currently only supports 16-bit timers" +#endif + +#if MYNEWT_VAL(TIMER_1) && (KINETIS_TIMERS_MAX < 2) +#error "This MCU does not support TIMER_1" +#endif + +/* + * This value for MCGIRCLK matches the current configuration for Kinetis BSPs + * where the slow clock is used and the divider is set to 1 (FCRDIV==0). + */ +#define MCGIRCLK_HZ 32768 + +struct kinetis_hal_tmr { + LPTMR_Type *base; + uint16_t overflow; /* MSB of the current counter is handled manually */ + uint16_t last_irq_cnt; /* Last counter on the interrupt handler */ + TAILQ_HEAD(hal_timer_qhead, hal_timer) hal_timer_q; +}; + +#if MYNEWT_VAL(TIMER_0) +struct kinetis_hal_tmr kinetis_tmr0 = { + .base = LPTMR0, +}; +#endif +#if MYNEWT_VAL(TIMER_1) +struct kinetis_hal_tmr kinetis_tmr1 = { + .base = LPTMR1, +}; +#endif + +static const IRQn_Type kinetis_timer_irqs[] = LPTMR_IRQS; + +static const struct kinetis_hal_tmr *kinetis_timers[KINETIS_TIMERS_MAX] = { +#if MYNEWT_VAL(TIMER_0) + &kinetis_tmr0, +#else + NULL, +#endif +#if MYNEWT_VAL(TIMER_1) + &kinetis_tmr1, +#else + NULL, +#endif +}; + +static uint32_t +kinetis_tmr_read(const struct kinetis_hal_tmr *tmr) +{ + return ((uint32_t)(tmr->overflow << 16) + LPTMR_GetCurrentTimerCount(tmr->base)); +} + +static void +kinetis_tmr_set_period(struct kinetis_hal_tmr *tmr, uint32_t tick) +{ + /* CMR cannot be zero, so add one cycle */ + if ((tick & 0xffff) == 0) { + tick++; + } + + /* XXX will only set the lower 16-bits */ + LPTMR_SetTimerPeriod(tmr->base, tick); +} + +static uint32_t +kinetis_tmr_get_freq(const struct kinetis_hal_tmr *tmr) +{ + uint32_t psr; + uint32_t freq; + + psr = tmr->base->PSR; + switch (psr & LPTMR_PSR_PCS_MASK) { + case 0: + freq = MCGIRCLK_HZ; + /* if the clock divisor is not bypassed, apply the divider */ + if (!(psr & LPTMR_PSR_PBYP_MASK)) { + freq >>= ((psr & LPTMR_PSR_PRESCALE_MASK) >> LPTMR_PSR_PRESCALE_SHIFT) + 1; + } + break; + default: + /* Only MCGIRCLK is supported at the moment */ + assert(0); + } + + return freq; +} + +static void +kinetis_tmr_config_freq(lptmr_config_t *config, uint32_t freq_hz) +{ + uint32_t tmr_freq; + uint8_t prescaler; + + config->prescalerClockSource = kLPTMR_PrescalerClock_0; + + tmr_freq = MCGIRCLK_HZ; + prescaler = 0xff; /* overflow on purpose */ + while (freq_hz < tmr_freq) { + tmr_freq /= 2; + prescaler++; + /* avoid a second overflow */ + if (prescaler == 0xff) { + break; + } + } + + if (tmr_freq != MCGIRCLK_HZ) { + config->bypassPrescaler = false; + config->value = prescaler; + } else { + config->bypassPrescaler = true; + config->value = 0; + } +} + +static void +timer_irq_handler(struct kinetis_hal_tmr *tmr) +{ + struct hal_timer *timer; + uint16_t cur_cnt; + + /* Check if 16-bit counter rotated */ + cur_cnt = (uint16_t)LPTMR_GetCurrentTimerCount(tmr->base); + if (cur_cnt <= tmr->last_irq_cnt) { + tmr->overflow++; + } + tmr->last_irq_cnt = cur_cnt; + + while ((timer = TAILQ_FIRST(&tmr->hal_timer_q)) != NULL) { + if ((int32_t)(kinetis_tmr_read(tmr) - timer->expiry) >= 0) { + TAILQ_REMOVE(&tmr->hal_timer_q, timer, link); + timer->link.tqe_prev = NULL; + timer->cb_func(timer->cb_arg); + } else { + break; + } + } + + timer = TAILQ_FIRST(&tmr->hal_timer_q); + if (timer) { + kinetis_tmr_set_period(tmr, timer->expiry); + } + + LPTMR_ClearStatusFlags(tmr->base, kLPTMR_TimerCompareFlag); +} + +#if (FSL_FEATURE_LPTMR_HAS_SHARED_IRQ_HANDLER != 0) +static void +timer0_1_irq_handler(void) +{ + const struct kinetis_hal_tmr *tmr; + int i; + + for (i = 0; i < KINETIS_TIMERS_MAX; i++) { + tmr = kinetis_timers[i]; + if (tmr && tmr->base->CSR & LPTMR_CSR_TCF_MASK) { + timer_irq_handler((struct kinetis_hal_tmr *)tmr); + } + } +} + +#else /* (FSL_FEATURE_LPTMR_HAS_SHARED_IRQ_HANDLER != 0) */ + +#if MYNEWT_VAL(TIMER_0) +static void +timer0_irq_handler(void) +{ + timer_irq_handler(&kinetis_tmr0); +} +#endif + +#if MYNEWT_VAL(TIMER_1) +static void +timer1_irq_handler(void) +{ + timer_irq_handler(&kinetis_tmr1); +} +#endif +#endif /* (FSL_FEATURE_LPTMR_HAS_SHARED_IRQ_HANDLER != 0) */ + +int +hal_timer_init(int num, void *cfg) +{ + const struct kinetis_hal_tmr *tmr; + lptmr_config_t default_config; + IRQn_Type irqn; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return -1; + } + + LPTMR_GetDefaultConfig(&default_config); + LPTMR_Init(tmr->base, &default_config); + + irqn = kinetis_timer_irqs[num]; + NVIC_SetPriority(irqn, (1 << __NVIC_PRIO_BITS) - 1); +#if (FSL_FEATURE_LPTMR_HAS_SHARED_IRQ_HANDLER != 0) + NVIC_SetVector(irqn, (uint32_t)timer0_1_irq_handler); +#else + switch (num) { +#if MYNEWT_VAL(TIMER_0) + case 0: + NVIC_SetVector(irqn, (uint32_t)timer0_irq_handler); + break; +#endif +#if MYNEWT_VAL(TIMER_1) + case 1: + NVIC_SetVector(irqn, (uint32_t)timer1_irq_handler); + break; +#endif + } +#endif /* (FSL_FEATURE_LPTMR_HAS_SHARED_IRQ_HANDLER != 0) */ + + NVIC_EnableIRQ(irqn); + + return 0; +} + +int +hal_timer_deinit(int num) +{ + const struct kinetis_hal_tmr *tmr; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return -1; + } + + LPTMR_Deinit(tmr->base); + + return 0; +} + +int +hal_timer_config(int num, uint32_t freq_hz) +{ + const struct kinetis_hal_tmr *tmr; + lptmr_config_t timer_config; + os_sr_t sr; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return -1; + } + + OS_ENTER_CRITICAL(sr); + + LPTMR_GetDefaultConfig(&timer_config); + kinetis_tmr_config_freq(&timer_config, freq_hz); + timer_config.enableFreeRunning = true; + LPTMR_StopTimer(tmr->base); + LPTMR_Init(tmr->base, &timer_config); + LPTMR_EnableInterrupts(tmr->base, kLPTMR_TimerInterruptEnable); + LPTMR_StartTimer(tmr->base); + + OS_EXIT_CRITICAL(sr); + + return 0; +} + +uint32_t +hal_timer_get_resolution(int num) +{ + const struct kinetis_hal_tmr *tmr; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return 0; + } + + return (1000000000 / kinetis_tmr_get_freq(tmr)); +} + +uint32_t +hal_timer_read(int num) +{ + const struct kinetis_hal_tmr *tmr; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return -1; + } + + return kinetis_tmr_read(tmr); +} + +int +hal_timer_delay(int num, uint32_t ticks) +{ + const struct kinetis_hal_tmr *tmr; + uint32_t until; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return -1; + } + + until = kinetis_tmr_read(tmr) + ticks; + while ((int32_t)(kinetis_tmr_read(tmr) - until) <= 0) { + ; + } + + return 0; +} + +int +hal_timer_set_cb(int num, struct hal_timer *timer, hal_timer_cb cb_func, + void *arg) +{ + const struct kinetis_hal_tmr *tmr; + + if (num >= KINETIS_TIMERS_MAX || !(tmr = kinetis_timers[num])) { + return -1; + } + + timer->cb_func = cb_func; + timer->cb_arg = arg; + timer->bsp_timer = (struct kinetis_hal_tmr *)tmr; + timer->link.tqe_prev = NULL; + + return 0; +} + +int +hal_timer_start(struct hal_timer *timer, uint32_t ticks) +{ + struct kinetis_hal_tmr *tmr; + uint32_t tick; + + tmr = (struct kinetis_hal_tmr *)timer->bsp_timer; + + tick = ticks + kinetis_tmr_read(tmr); + return hal_timer_start_at(timer, tick); +} + +int +hal_timer_start_at(struct hal_timer *timer, uint32_t tick) +{ + struct kinetis_hal_tmr *tmr; + struct hal_timer *entry; + os_sr_t sr; + + if (!timer || timer->link.tqe_prev || !timer->cb_func) { + return -1; + } + + tmr = (struct kinetis_hal_tmr *)timer->bsp_timer; + timer->expiry = tick; + + OS_ENTER_CRITICAL(sr); + + if (TAILQ_EMPTY(&tmr->hal_timer_q)) { + TAILQ_INSERT_HEAD(&tmr->hal_timer_q, timer, link); + } else { + TAILQ_FOREACH(entry, &tmr->hal_timer_q, link) { + if ((int32_t)(timer->expiry - entry->expiry) < 0) { + TAILQ_INSERT_BEFORE(entry, timer, link); + break; + } + } + if (!entry) { + TAILQ_INSERT_TAIL(&tmr->hal_timer_q, timer, link); + } + } + + if (timer == TAILQ_FIRST(&tmr->hal_timer_q)) { + kinetis_tmr_set_period(tmr, tick); + } + + LPTMR_EnableInterrupts(tmr->base, kLPTMR_TimerInterruptEnable); + LPTMR_StartTimer(tmr->base); + + OS_EXIT_CRITICAL(sr); + + return 0; +} + +int +hal_timer_stop(struct hal_timer *timer) +{ + struct kinetis_hal_tmr *tmr; + struct hal_timer *entry; + bool reset_period; + os_sr_t sr; + + tmr = (struct kinetis_hal_tmr *)timer->bsp_timer; + + OS_ENTER_CRITICAL(sr); + + if (timer->link.tqe_prev != NULL) { + reset_period = false; + if (timer == TAILQ_FIRST(&tmr->hal_timer_q)) { + entry = TAILQ_NEXT(timer, link); + reset_period = true; + } + TAILQ_REMOVE(&tmr->hal_timer_q, timer, link); + timer->link.tqe_prev = NULL; + if (reset_period) { + if (entry) { + kinetis_tmr_set_period((struct kinetis_hal_tmr *)entry->bsp_timer, + entry->expiry); + } else { + LPTMR_StopTimer(tmr->base); + } + } + } + + OS_EXIT_CRITICAL(sr); + + return 0; +} diff --git a/hw/mcu/nxp/kinetis/syscfg.yml b/hw/mcu/nxp/kinetis/syscfg.yml index 3e245d4..baf9a60 100644 --- a/hw/mcu/nxp/kinetis/syscfg.yml +++ b/hw/mcu/nxp/kinetis/syscfg.yml @@ -36,6 +36,13 @@ syscfg.defs: description: 'Enable hash accelerator (CAU)' value: 0 + TIMER_0: + description: 'Enable Timer 0' + value: 1 + TIMER_1: + description: 'Enable Timer 1' + value: 0 + GPIO_MAX_IRQ: description: 'Max number of GPIO pins assigned to IRQs' value: 10