blob: 80d81ddc3df4922b32c74501b60e0081ae25077a [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 <stdint.h>
#include <assert.h>
#include "os/mynewt.h"
#include "hal/hal_timer.h"
/*
* For native cpu implementation.
*/
static uint8_t native_timer_task_started;
#define NATIVE_TIMER_STACK_SIZE (1024)
static os_stack_t native_timer_stack[NATIVE_TIMER_STACK_SIZE];
static struct os_task native_timer_task_struct;
static struct os_eventq native_timer_evq;
struct native_timer {
struct os_callout callout;
uint32_t ticks_per_ostick;
uint32_t cnt;
uint32_t last_ostime;
int num;
TAILQ_HEAD(hal_timer_qhead, hal_timer) timers;
} native_timers[1];
/**
* This is the function called when the timer fires.
*
* @param arg
*/
static void
native_timer_cb(struct os_event *ev)
{
struct native_timer *nt = (struct native_timer *)ev->ev_arg;
uint32_t cnt;
struct hal_timer *ht;
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
cnt = hal_timer_read(nt->num);
while ((ht = TAILQ_FIRST(&nt->timers)) != NULL) {
if (((int32_t)(cnt - ht->expiry)) >= 0) {
TAILQ_REMOVE(&nt->timers, ht, link);
ht->link.tqe_prev = NULL;
ht->cb_func(ht->cb_arg);
} else {
break;
}
}
ht = TAILQ_FIRST(&nt->timers);
if (ht) {
os_callout_reset(&nt->callout,
(ht->expiry - hal_timer_read(nt->num)) / nt->ticks_per_ostick);
}
OS_EXIT_CRITICAL(sr);
}
static void
native_timer_task(void *arg)
{
while (1) {
os_eventq_run(&native_timer_evq);
}
}
int
hal_timer_init(int num, void *cfg)
{
return 0;
}
int
hal_timer_config(int num, uint32_t clock_freq)
{
struct native_timer *nt;
if (num != 0) {
return -1;
}
nt = &native_timers[num];
/* Set the clock frequency */
nt->ticks_per_ostick = clock_freq / OS_TICKS_PER_SEC;
if (!nt->ticks_per_ostick) {
nt->ticks_per_ostick = 1;
}
nt->num = num;
nt->cnt = 0;
nt->last_ostime = os_time_get();
if (!native_timer_task_started) {
os_task_init(&native_timer_task_struct, "native_timer",
native_timer_task, NULL, OS_TASK_PRI_HIGHEST, OS_WAIT_FOREVER,
native_timer_stack, NATIVE_TIMER_STACK_SIZE);
/* Initialize the eventq and task */
os_eventq_init(&native_timer_evq);
native_timer_task_started = 1;
}
/* Initialize the callout function */
os_callout_init(&nt->callout, &native_timer_evq, native_timer_cb, nt);
return 0;
}
int
hal_timer_deinit(int num)
{
struct native_timer *nt;
if (num != 0) {
return -1;
}
nt = &native_timers[num];
os_callout_stop(&nt->callout);
return 0;
}
/**
* hal timer get resolution
*
* Get the resolution of the timer. This is the timer period, in nanoseconds
*
* @param timer_num
*
* @return uint32_t
*/
uint32_t
hal_timer_get_resolution(int num)
{
struct native_timer *nt;
if (num >= 0) {
return 0;
}
nt = &native_timers[num];
return 1000000000 / (nt->ticks_per_ostick * OS_TICKS_PER_SEC);
}
/**
* hal_timer_read
*
* Returns the low 32 bits of timer counter.
*
* @return uint32_t The timer counter register.
*/
uint32_t
hal_timer_read(int num)
{
struct native_timer *nt;
os_sr_t sr;
uint32_t ostime;
uint32_t delta_osticks;
if (num != 0) {
return -1;
}
nt = &native_timers[num];
OS_ENTER_CRITICAL(sr);
ostime = os_time_get();
delta_osticks = (uint32_t)(ostime - nt->last_ostime);
if (delta_osticks) {
nt->last_ostime = ostime;
nt->cnt += nt->ticks_per_ostick * delta_osticks;
}
OS_EXIT_CRITICAL(sr);
return (uint32_t)nt->cnt;
}
/**
* hal timer delay
*
* Blocking delay for n ticks
*
* @param timer_num
* @param ticks
*
* @return int 0 on success; error code otherwise.
*/
int
hal_timer_delay(int num, uint32_t ticks)
{
uint32_t until;
if (num != 0) {
return -1;
}
until = hal_timer_read(0) + ticks;
while ((int32_t)(hal_timer_read(0) - until) <= 0) {
;
}
return 0;
}
/**
*
* Initialize the HAL timer structure with the callback and the callback
* argument. Also initializes the HW specific timer pointer.
*
* @param cb_func
*
* @return int
*/
int
hal_timer_set_cb(int num, struct hal_timer *timer, hal_timer_cb cb_func,
void *arg)
{
struct native_timer *nt;
if (num != 0) {
return -1;
}
nt = &native_timers[num];
timer->cb_func = cb_func;
timer->cb_arg = arg;
timer->bsp_timer = nt;
timer->link.tqe_prev = NULL;
return 0;
}
/**
* hal_timer_start()
*
* Start a timer. Timer fires 'ticks' ticks from now.
*
* @param timer
* @param ticks
*
* @return int
*/
int
hal_timer_start(struct hal_timer *timer, uint32_t ticks)
{
struct native_timer *nt;
uint32_t tick;
nt = (struct native_timer *)timer->bsp_timer;
tick = ticks + hal_timer_read(nt->num);
return hal_timer_start_at(timer, tick);
}
/**
* hal_timer_start_at()
*
* Start a timer. Timer fires at tick 'tick'.
*
* @param timer
* @param tick
*
* @return int
*/
int
hal_timer_start_at(struct hal_timer *timer, uint32_t tick)
{
struct native_timer *nt;
struct hal_timer *ht;
uint32_t curtime;
uint32_t osticks;
os_sr_t sr;
nt = (struct native_timer *)timer->bsp_timer;
timer->expiry = tick;
OS_ENTER_CRITICAL(sr);
if (TAILQ_EMPTY(&nt->timers)) {
TAILQ_INSERT_HEAD(&nt->timers, timer, link);
} else {
TAILQ_FOREACH(ht, &nt->timers, link) {
if ((int32_t)(timer->expiry - ht->expiry) < 0) {
TAILQ_INSERT_BEFORE(ht, timer, link);
break;
}
}
if (!ht) {
TAILQ_INSERT_TAIL(&nt->timers, timer, link);
}
}
curtime = hal_timer_read(nt->num);
if ((int32_t)(tick - curtime) <= 0) {
/*
* Event in the past (should be the case if it was just inserted).
*/
os_callout_reset(&nt->callout, 0);
} else {
if (timer == TAILQ_FIRST(&nt->timers)) {
osticks = (tick - curtime) / nt->ticks_per_ostick;
os_callout_reset(&nt->callout, osticks);
}
}
OS_EXIT_CRITICAL(sr);
return 0;
}
/**
* hal_timer_stop()
*
* Cancels the timer.
*
* @param timer
*
* @return int
*/
int
hal_timer_stop(struct hal_timer *timer)
{
struct native_timer *nt;
struct hal_timer *ht;
int reset_ocmp;
os_sr_t sr;
OS_ENTER_CRITICAL(sr);
nt = (struct native_timer *)timer->bsp_timer;
if (timer->link.tqe_prev != NULL) {
reset_ocmp = 0;
if (timer == TAILQ_FIRST(&nt->timers)) {
/* If first on queue, we will need to reset OCMP */
ht = TAILQ_NEXT(timer, link);
reset_ocmp = 1;
}
TAILQ_REMOVE(&nt->timers, timer, link);
timer->link.tqe_prev = NULL;
if (reset_ocmp) {
if (ht) {
os_callout_reset(&nt->callout,
(ht->expiry - hal_timer_read(nt->num)) /
nt->ticks_per_ostick);
} else {
os_callout_stop(&nt->callout);
}
}
}
OS_EXIT_CRITICAL(sr);
return 0;
}