/* * Copyright (c) 2016-2018, Arm Limited and affiliates. * SPDX-License-Identifier: Apache-2.0 * * Licensed 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 before mbed.h to properly get UINT*_C() #include "ns_types.h" #include "platform/SingletonPtr.h" #include "platform/arm_hal_timer.h" #include "platform/arm_hal_interrupt.h" #include "platform/mbed_assert.h" #include "Timeout.h" #include "events/Event.h" #include "events/mbed_shared_queues.h" using namespace mbed; using namespace events; using std::ratio; using std::milli; using std::micro; using namespace std::chrono; static SingletonPtr<Timeout> timeout; // If critical sections are implemented using mutexes, timers must be called in thread context, and // we use the high-priority event queue for this. // If critical sections disable interrupts, we can call timers directly from interrupt. Avoiding the // event queue can save ~1600B of RAM if the rest of the system is not using the event queue either. // Caveats of this tunable are listed on arm_hal_interrupt.c. #if !MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT static EventQueue *equeue; #endif namespace { using slot_period = std::ratio_multiply<ratio<50>, micro>; using slots_t = duration<uint32_t, slot_period>; } static HighResClock::time_point due; static void (*arm_hal_callback)(void); #if defined(NS_EVENTLOOP_USE_TICK_TIMER) #if MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT static SingletonPtr<Ticker> tick_ticker; #endif static int tick_timer_id; static void (*tick_timer_cb)(void); int8_t platform_tick_timer_register(void (*tick_timer_cb_handler)(void)) { #if !MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT equeue = mbed_highprio_event_queue(); MBED_ASSERT(equeue != NULL); #endif tick_timer_cb = tick_timer_cb_handler; return 0; } int8_t platform_tick_timer_start(uint32_t period_ms) { int8_t retval = -1; auto period = duration<uint32_t, milli>(period_ms); if (tick_timer_cb && tick_timer_id == 0) { #if !MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT tick_timer_id = equeue->call_every(period, tick_timer_cb); if (tick_timer_id != 0) { retval = 0; } #else tick_ticker->attach(tick_timer_cb, period); tick_timer_id = 1; retval = 0; #endif } return retval; } int8_t platform_tick_timer_stop(void) { int8_t retval = -1; if (tick_timer_id != 0) { #if !MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT equeue->cancel(tick_timer_id); #else tick_ticker->detach(); #endif tick_timer_id = 0; retval = 0; } return retval; } #endif // NS_EVENTLOOP_USE_TICK_TIMER // Called once at boot void platform_timer_enable(void) { #if !MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT equeue = mbed_highprio_event_queue(); MBED_ASSERT(equeue != NULL); #endif // Prime the SingletonPtr - can't construct from IRQ/critical section timeout.get(); } // Actually cancels a timer, not the opposite of enable void platform_timer_disable(void) { timeout->detach(); } // Not called while running, fortunately void platform_timer_set_cb(void (*new_fp)(void)) { arm_hal_callback = new_fp; } static void timer_callback(void) { due = HighResClock::time_point{}; #if MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT // Callback is interrupt safe so it can be called directly without // bouncing via event queue thread. arm_hal_callback(); #else equeue->call(arm_hal_callback); #endif } // This is called from inside platform_enter_critical - IRQs can't happen void platform_timer_start(uint16_t slots) { slots_t rel_time{slots}; due = HighResClock::now() + rel_time; timeout->attach_absolute(timer_callback, due); } // This is called from inside platform_enter_critical - IRQs can't happen uint16_t platform_timer_get_remaining_slots(void) { auto now = HighResClock::now(); if (now < due) { return duration_cast<slots_t>(due - now).count(); } else { return 0; } }