blob: 64bc53653bd4ed122845f5d9176539f93dadf123 [file] [log] [blame]
/*
* 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 <assert.h>
#include "mcu/da1469x_clock.h"
#include "mcu/da1469x_hal.h"
#include "mcu/cmsis_nvic.h"
#include "hal/hal_os_tick.h"
#include "hal/hal_system.h"
#include "os/os_trace_api.h"
#include "da1469x_priv.h"
#include "mcu/mcu.h"
struct hal_os_tick {
int ticks_per_ostick;
os_time_t max_idle_ticks;
uint32_t last_trigger_val;
};
struct hal_os_tick g_hal_os_tick;
/*
* Implement (x - y) where the range of both 'x' and 'y' is limited to 24-bits.
*
* For example:
*
* sub24(0, 0xffffff) = 1
* sub24(0xffffff, 0xfffffe) = 1
* sub24(0xffffff, 0) = -1
* sub24(0x7fffff, 0) = 8388607
* sub24(0x800000, 0) = -8388608
*/
static inline int
sub24(uint32_t x, uint32_t y)
{
int result;
assert(x <= 0xffffff);
assert(y <= 0xffffff);
result = x - y;
if (result & 0x800000) {
return (result | 0xff800000);
} else {
return (result & 0x007fffff);
}
}
static inline uint32_t
hal_os_tick_get_timer_val(void)
{
return TIMER2->TIMER2_TIMER_VAL_REG;
}
static inline void
hal_os_tick_set_timer_trigger_val(uint32_t trigger_val)
{
uint32_t timer_val;
int delta;
while (1) {
trigger_val &= 0xffffff;
TIMER2->TIMER2_RELOAD_REG = trigger_val;
timer_val = hal_os_tick_get_timer_val();
/* XXX Not sure what happens if we set TIMER2_RELOAD_REG to the same
* value as TIMER2_TIMER_VAL_REG, so just in case this does not
* trigger interrupt let's just move to next tick.
*/
delta = sub24(trigger_val, timer_val);
if (delta > 0) {
break;
}
trigger_val += g_hal_os_tick.ticks_per_ostick;
}
}
static void
hal_os_tick_handler(void)
{
uint32_t primask;
uint32_t timer_val;
int delta;
int ticks;
__HAL_DISABLE_INTERRUPTS(primask);
/* Calculate elapsed ticks and advance OS time. */
timer_val = hal_os_tick_get_timer_val();
delta = sub24(timer_val, g_hal_os_tick.last_trigger_val);
ticks = delta / g_hal_os_tick.ticks_per_ostick;
os_time_advance(ticks);
/* Clear timer interrupt */
TIMER2->TIMER2_CLEAR_IRQ_REG = 1;
/* Update the time associated with the most recent tick */
g_hal_os_tick.last_trigger_val = (g_hal_os_tick.last_trigger_val +
(ticks * g_hal_os_tick.ticks_per_ostick)) &
0xffffff;
/* Update timer trigger value for interrupt at the next tick */
hal_os_tick_set_timer_trigger_val(g_hal_os_tick.last_trigger_val +
g_hal_os_tick.ticks_per_ostick);
__HAL_ENABLE_INTERRUPTS(primask);
}
static void
hal_os_tick_timer2_isr(void)
{
os_trace_isr_enter();
hal_os_tick_handler();
os_trace_isr_exit();
}
void
os_tick_idle(os_time_t ticks)
{
uint32_t new_trigger_val;
OS_ASSERT_CRITICAL();
if (ticks > 0) {
if (ticks > g_hal_os_tick.max_idle_ticks) {
ticks = g_hal_os_tick.max_idle_ticks;
}
new_trigger_val = g_hal_os_tick.last_trigger_val +
(ticks * g_hal_os_tick.ticks_per_ostick);
hal_os_tick_set_timer_trigger_val(new_trigger_val);
}
da1469x_sleep(ticks);
if (ticks > 0) {
hal_os_tick_handler();
}
}
void
os_tick_init(uint32_t os_ticks_per_sec, int prio)
{
uint32_t primask;
g_hal_os_tick.last_trigger_val = 0;
#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX)
g_hal_os_tick.ticks_per_ostick = da1469x_clock_lp_rcx_freq_get() / os_ticks_per_sec;
#else
g_hal_os_tick.ticks_per_ostick = 32768 / os_ticks_per_sec;
#endif
g_hal_os_tick.max_idle_ticks = (1UL << 22) / g_hal_os_tick.ticks_per_ostick;
TIMER2->TIMER2_CTRL_REG = 0;
TIMER2->TIMER2_PRESCALER_REG = 0;
TIMER2->TIMER2_CTRL_REG |= TIMER2_TIMER2_CTRL_REG_TIM_CLK_EN_Msk;
TIMER2->TIMER2_CTRL_REG |= (TIMER2_TIMER2_CTRL_REG_TIM_FREE_RUN_MODE_EN_Msk |
TIMER2_TIMER2_CTRL_REG_TIM_IRQ_EN_Msk |
TIMER2_TIMER2_CTRL_REG_TIM_EN_Msk);
__HAL_DISABLE_INTERRUPTS(primask);
NVIC_SetPriority(TIMER2_IRQn, prio);
NVIC_SetVector(TIMER2_IRQn, (uint32_t)hal_os_tick_timer2_isr);
NVIC_EnableIRQ(TIMER2_IRQn);
__HAL_ENABLE_INTERRUPTS(primask);
}