blob: 3897eccb943a7f8d2f17f05b6634f131c5fd7c65 [file] [log] [blame]
/*
* Copyright (c) 2019, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the OpenThread platform abstraction for the alarm.
*
*/
/* Openthread configuration */
#include OPENTHREAD_PROJECT_CORE_CONFIG_FILE
#include "TMR_Adapter.h"
#include "fsl_clock.h"
#include "fsl_ctimer.h"
#include "fsl_device_registers.h"
#include "fsl_wtimer.h"
#include "openthread-system.h"
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/diag.h>
#define ALARM_USE_CTIMER 0
#define ALARM_USE_WTIMER 1
/* Timer frequency in Hz needed for 1ms tick */
#define TARGET_FREQ 1000U
/* Wake Timer max count value that is loaded in the register */
#define TIMER0_MAX_COUNT_VALUE 0xffffffff
#define TIMER1_MAX_COUNT_VALUE 0x0fffffff
static bool sEventFired = false;
static uint32_t refClk;
#if ALARM_USE_CTIMER
/* Match Configuration for Channel 0 */
static ctimer_match_config_t sMatchConfig = {.enableCounterReset = false,
.enableCounterStop = false,
.matchValue = 0x00,
.outControl = kCTIMER_Output_NoAction,
.outPinInitState = false,
.enableInterrupt = true};
#else
static uint32_t sRemainingTicks;
#endif
void K32WAlarmInit(void)
{
#if ALARM_USE_CTIMER
ctimer_config_t config;
CTIMER_GetDefaultConfig(&config);
/* Get clk frequency and use prescale to lower it */
refClk = CLOCK_GetFreq(kCLOCK_Timer0);
config.prescale = refClk / TARGET_FREQ;
CTIMER_Init(CTIMER0, &config);
CTIMER_StartTimer(CTIMER0);
CTIMER_EnableInterrupts(CTIMER0, kCTIMER_Match0InterruptEnable);
NVIC_ClearPendingIRQ(Timer0_IRQn);
NVIC_EnableIRQ(Timer0_IRQn);
#else
RESET_PeripheralReset(kWKT_RST_SHIFT_RSTn);
WTIMER_Init();
/* Get clk frequency and use prescale to lower it */
refClk = CLOCK_GetFreq(kCLOCK_Xtal32k);
/* Wake timer 0 is 41 bits long and is used for keepig the timestamp */
WTIMER_EnableInterrupts(WTIMER_TIMER0_ID);
/* Wake timer 1 is 28 bits long and is used for alarm events, including waking up the MCU
from sleep */
WTIMER_EnableInterrupts(WTIMER_TIMER1_ID);
NVIC_SetPriority(WAKE_UP_TIMER0_IRQn, gStackTimer_IsrPrio_c >> (8 - __NVIC_PRIO_BITS));
NVIC_SetPriority(WAKE_UP_TIMER1_IRQn, gStackTimer_IsrPrio_c >> (8 - __NVIC_PRIO_BITS));
/* Start wake timer 0 counter for timestamp - the counter counts down to 0 so a simple
substracion from TIMER0_MAX_COUNT_VALUE will give us the timestamp */
WTIMER_StartTimer(WTIMER_TIMER0_ID, TIMER0_MAX_COUNT_VALUE);
#endif
}
void K32WAlarmClean(void)
{
#if ALARM_USE_CTIMER
CTIMER_StopTimer(CTIMER0);
CTIMER_Deinit(CTIMER0);
CTIMER_DisableInterrupts(CTIMER0, kCTIMER_Match0InterruptEnable);
NVIC_ClearPendingIRQ(Timer0_IRQn);
#else
WTIMER_StopTimer(WTIMER_TIMER0_ID);
WTIMER_StopTimer(WTIMER_TIMER1_ID);
WTIMER_DeInit();
NVIC_DisableIRQ(WAKE_UP_TIMER0_IRQn);
NVIC_ClearPendingIRQ(WAKE_UP_TIMER0_IRQn);
NVIC_DisableIRQ(WAKE_UP_TIMER1_IRQn);
NVIC_ClearPendingIRQ(WAKE_UP_TIMER1_IRQn);
#endif
}
void K32WAlarmProcess(otInstance *aInstance)
{
if (sEventFired)
{
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagAlarmFired(aInstance);
}
else
#endif
{
otPlatAlarmMilliFired(aInstance);
}
}
}
void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt)
{
OT_UNUSED_VARIABLE(aInstance);
#if ALARM_USE_CTIMER
/* Load match register with current counter + app time */
sMatchConfig.matchValue = aT0 + aDt;
CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_0, &sMatchConfig);
#else
/* Calculate the difference between now and the requested timestamp aT0 - this time will be
substracted from the total time until the event needs to fire */
uint32_t timestamp = otPlatAlarmMilliGetNow();
timestamp = timestamp - aT0;
uint64_t targetTicks = ((aDt - timestamp) * refClk) / TARGET_FREQ;
/* Because timer 1 is only 28 bits long we need to take into account and event longer than this
so we arm the timer with the maximum value and re-arm with the remaing time once it fires */
if (targetTicks < TIMER1_MAX_COUNT_VALUE)
{
WTIMER_StartTimer(WTIMER_TIMER1_ID, targetTicks);
sRemainingTicks = 0;
}
else
{
WTIMER_StartTimer(WTIMER_TIMER1_ID, TIMER1_MAX_COUNT_VALUE);
sRemainingTicks = targetTicks - TIMER1_MAX_COUNT_VALUE;
}
#endif
}
void otPlatAlarmMilliStop(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
sEventFired = false;
#if ALARM_USE_CTIMER
sMatchConfig.matchValue = 0;
CTIMER_SetupMatch(CTIMER0, kCTIMER_Match_0, &sMatchConfig);
#else
sRemainingTicks = 0;
WTIMER_StopTimer(WTIMER_TIMER1_ID);
#endif
}
uint32_t otPlatAlarmMilliGetNow(void)
{
#if ALARM_USE_CTIMER
return CTIMER0->TC;
#else
uint32_t timestamp = WTIMER_ReadTimerSafe(WTIMER_TIMER0_ID);
uint64_t tempTstamp = (TIMER0_MAX_COUNT_VALUE - timestamp);
tempTstamp *= TARGET_FREQ;
tempTstamp /= refClk;
return (uint32_t)tempTstamp;
#endif
}
#if ALARM_USE_CTIMER
/**
* Timer interrupt handler function.
*
*/
void CTIMER0_IRQHandler(void)
{
uint32_t flags = CTIMER_GetStatusFlags(CTIMER0);
CTIMER_ClearStatusFlags(CTIMER0, flags);
sEventFired = true;
#if USE_RTOS
otSysEventSignalPending();
#endif
}
#else
void WAKE_UP_TIMER0_DriverIRQHandler()
{
WTIMER_ClearStatusFlags(WTIMER_TIMER0_ID);
WTIMER_StartTimer(WTIMER_TIMER0_ID, TIMER0_MAX_COUNT_VALUE);
#if USE_RTOS
otSysEventSignalPending();
#endif
}
void WAKE_UP_TIMER1_DriverIRQHandler()
{
WTIMER_ClearStatusFlags(WTIMER_TIMER1_ID);
if (sRemainingTicks)
{
WTIMER_StartTimer(WTIMER_TIMER1_ID, sRemainingTicks);
sRemainingTicks = 0;
}
else
{
sEventFired = true;
}
#if USE_RTOS
otSysEventSignalPending();
#endif
}
#endif