blob: f93cf972c5a91738f97493bf67e47aa2e14a004e [file] [log] [blame]
/**
****************************************************************************************
*
* @file power.c
*
* @brief FTDF power on/off functions
*
* Copyright (c) 2016, Dialog Semiconductor
* 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.
*
****************************************************************************************
*/
#include <stdlib.h>
#include <ftdf.h>
#include "internal.h"
#include "regmap.h"
static FTDF_PSec FTDF_lowPowerClockCycle __attribute__((section(".retention")));
static FTDF_PSec FTDF_wakeUpLatency __attribute__((section(".retention")));
/* Pre-calculated value for optimization. */
static FTDF_USec FTDF_lowPowerClockCycleUSec __attribute__((section(".retention")));
/* Pre-calculated value for optimization. */
static FTDF_USec FTDF_wakeUpLatencyUSec __attribute__((section(".retention")));
/* Pre-calculated value for optimization. */
static FTDF_NrLowPowerClockCycles FTDF_csmacaWakeupThr __attribute__((section(".retention")));
#if !defined(FTDF_NO_CSL) || !defined(FTDF_NO_TSCH)
static uint32_t FTDF_eventCurrVal __attribute__((section(".retention")));
static uint32_t FTDF_timeStampCurrVal __attribute__((section(".retention")));
static uint32_t FTDF_timeStampCurrPhaseVal __attribute__((section(".retention")));
#endif
#ifndef FTDF_NO_CSL
FTDF_Boolean FTDF_wakeUpEnableLe __attribute__((section(".retention")));
#endif /* FTDF_NO_CSL */
#ifndef FTDF_NO_TSCH
FTDF_Boolean FTDF_wakeUpEnableTsch;
#endif /* FTDF_NO_TSCH */
void FTDF_setSleepAttributes(FTDF_PSec lowPowerClockCycle,
FTDF_NrLowPowerClockCycles wakeUpLatency)
{
FTDF_lowPowerClockCycle = lowPowerClockCycle;
FTDF_wakeUpLatency = (uint64_t)wakeUpLatency * lowPowerClockCycle;
FTDF_lowPowerClockCycleUSec = FTDF_lowPowerClockCycle / 1000000;
FTDF_wakeUpLatencyUSec = FTDF_wakeUpLatency / 1000000;
FTDF_csmacaWakeupThr = (FTDF_NrLowPowerClockCycles)
(((FTDF_PSec) 0xffffffff * 1000000 - FTDF_wakeUpLatency) / FTDF_lowPowerClockCycle);
}
FTDF_USec FTDF_canSleep(void)
{
#ifdef FTDF_PHY_API
if (FTDF_txInProgress || FTDF_pib.keepPhyEnabled)
{
return 0;
}
#else
#if FTDF_USE_SLEEP_DURING_BACKOFF
if (FTDF_pib.keepPhyEnabled)
#else
if (FTDF_reqCurrent || FTDF_pib.keepPhyEnabled)
#endif /* FTDF_USE_SLEEP_DURING_BACKOFF */
{
return 0;
}
#endif
#if FTDF_USE_SLEEP_DURING_BACKOFF
if (FTDF_GET_FIELD(ON_OFF_REGMAP_SECBUSY) == 1)
#else /* FTDF_USE_SLEEP_DURING_BACKOFF */
if (FTDF_GET_FIELD(ON_OFF_REGMAP_LMACREADY4SLEEP) == 0 ||
FTDF_GET_FIELD(ON_OFF_REGMAP_SECBUSY) == 1)
#endif /* FTDF_USE_SLEEP_DURING_BACKOFF */
{
return 0;
}
#ifndef FTDF_NO_CSL
if (FTDF_pib.leEnabled)
{
#if FTDF_USE_SLEEP_DURING_BACKOFF
/* Abort sleep when LMAC is busy. */
if (FTDF_GET_FIELD(ON_OFF_REGMAP_LMACREADY4SLEEP) == 0)
{
return 0;
}
#endif /* FTDF_USE_SLEEP_DURING_BACKOFF */
if (FTDF_txInProgress == FTDF_FALSE)
{
FTDF_Time curTime = FTDF_GET_FIELD(ON_OFF_REGMAP_SYMBOLTIMESNAPSHOTVAL);
FTDF_Time delta = curTime - FTDF_startCslSampleTime;
// A delta larger than 0x80000000 is assumed to be negative
// Do not return a sleep value when CSL sample time is in the past
if (delta < 0x80000000)
{
return 0;
}
return (FTDF_startCslSampleTime - curTime) * 16;
}
else
{
return 0;
}
}
#endif /* FTDF_NO_CSL */
#ifndef FTDF_NO_TSCH
if (FTDF_pib.tschEnabled)
{
#if FTDF_USE_SLEEP_DURING_BACKOFF
/* Abort sleep when LMAC is busy. */
if (FTDF_GET_FIELD(ON_OFF_REGMAP_LMACREADY4SLEEP) == 0)
{
return 0;
}
#endif
FTDF_Time64 curTime64 = FTDF_getCurTime64();
FTDF_Time64 delta = curTime64 - FTDF_tschSlotTime;
// A delta larger than 0x8000000000000000 is assumed to be negative
// Do not return a sleep value when TSCH slot time is in the past
if (delta < 0x8000000000000000ULL)
{
return 0;
}
FTDF_USec sleepTime = (FTDF_USec)(FTDF_tschSlotTime - curTime64);
FTDF_Time pendListTime;
FTDF_Time curTime = FTDF_GET_FIELD(ON_OFF_REGMAP_SYMBOLTIMESNAPSHOTVAL);
FTDF_Boolean pending = FTDF_getTxPendingTimerHead(&pendListTime);
if (pending)
{
// A delta larger than 0x80000000 is assumed to be negative
// Do not return a sleep value when pending timer time is in the past
if (curTime - pendListTime < 0x80000000)
{
return 0;
}
FTDF_USec tmpSleepTime = (FTDF_USec)(pendListTime - curTime);
if (tmpSleepTime < sleepTime)
{
sleepTime = tmpSleepTime;
}
}
if (sleepTime < FTDF_TSCH_MAX_PROCESS_REQUEST_TIME + FTDF_TSCH_MAX_SCHEDULE_TIME)
{
return 0;
}
return (sleepTime - (FTDF_TSCH_MAX_PROCESS_REQUEST_TIME + FTDF_TSCH_MAX_SCHEDULE_TIME)) * 16;
}
#endif /* FTDF_NO_TSCH */
#ifndef FTDF_LITE
// Normal mode
int n;
for (n = 0; n < FTDF_NR_OF_REQ_BUFFERS; n++)
{
if (FTDF_txPendingList[ n ].addrMode != FTDF_NO_ADDRESS)
{
return 0;
}
}
#endif /* !FTDF_LITE */
#if FTDF_USE_SLEEP_DURING_BACKOFF
return FTDF_sdbGetSleepTime();
#else /* FTDF_USE_SLEEP_DURING_BACKOFF */
return 0xffffffff;
#endif /* FTDF_USE_SLEEP_DURING_BACKOFF */
}
FTDF_Boolean FTDF_prepareForSleep(FTDF_USec sleepTime)
{
#if !defined(FTDF_NO_CSL) || !defined(FTDF_NO_TSCH)
if (FTDF_pib.leEnabled || FTDF_pib.tschEnabled)
{
if (sleepTime < 2 * FTDF_lowPowerClockCycleUSec)
{
return FTDF_FALSE;
}
// Correct sleeptime with the inaccuracy of this function
sleepTime -= 2 * FTDF_lowPowerClockCycleUSec;
if (sleepTime < FTDF_wakeUpLatencyUSec + 500)
{
return FTDF_FALSE;
}
}
#endif
FTDF_criticalVar();
FTDF_enterCritical();
#if !defined(FTDF_NO_CSL) || !defined(FTDF_NO_TSCH)
// Capture the current value of both the event generator and the timestamp generator
// and phase on the rising edge of LP_CLK
FTDF_SET_FIELD(ON_OFF_REGMAP_GETGENERATORVAL, 1);
#endif
// Save current LMAC PM counters
FTDF_lmacCounters.fcsErrorCnt += FTDF_GET_FIELD(ON_OFF_REGMAP_MACFCSERRORCOUNT);
FTDF_lmacCounters.txStdAckCnt += FTDF_GET_FIELD(ON_OFF_REGMAP_MACTXSTDACKFRMCNT);
FTDF_lmacCounters.rxStdAckCnt += FTDF_GET_FIELD(ON_OFF_REGMAP_MACRXSTDACKFRMOKCNT);
#if !defined(FTDF_NO_CSL) || !defined(FTDF_NO_TSCH)
volatile uint32_t *getGeneratorValE = FTDF_GET_FIELD_ADDR(ON_OFF_REGMAP_GETGENERATORVAL_E);
// Wait until data is ready
while ((*getGeneratorValE & MSK_F_FTDF_ON_OFF_REGMAP_GETGENERATORVAL_E) == 0)
{ }
FTDF_eventCurrVal = FTDF_GET_FIELD(ON_OFF_REGMAP_EVENTCURRVAL);
FTDF_timeStampCurrVal = FTDF_GET_FIELD(ON_OFF_REGMAP_TIMESTAMPCURRVAL);
FTDF_timeStampCurrPhaseVal = FTDF_GET_FIELD(ON_OFF_REGMAP_TIMESTAMPCURRPHASEVAL);
#ifdef SIMULATOR
*getGeneratorValE &= ~MSK_F_FTDF_ON_OFF_REGMAP_GETGENERATORVAL_E;
#else
*getGeneratorValE = MSK_F_FTDF_ON_OFF_REGMAP_GETGENERATORVAL_E;
#endif
uint64_t nextWakeUpThr;
#if FTDF_USE_SLEEP_DURING_BACKOFF
uint64_t picoSleepTime = (uint64_t)sleepTime * 1000000;
nextWakeUpThr = (picoSleepTime - FTDF_wakeUpLatency) / FTDF_lowPowerClockCycle;
#else /* FTDF_USE_SLEEP_DURING_BACKOFF */
if (FTDF_pib.leEnabled || FTDF_pib.tschEnabled)
{
uint64_t picoSleepTime = (uint64_t)sleepTime * 1000000;
nextWakeUpThr = (picoSleepTime - FTDF_wakeUpLatency) / FTDF_lowPowerClockCycle;
}
else
{
nextWakeUpThr = FTDF_csmacaWakeupThr;
}
#endif /* FTDF_USE_SLEEP_DURING_BACKOFF */
// Set wake up threshold
uint32_t wakeUpIntThr = FTDF_eventCurrVal + nextWakeUpThr;
// Note that for IC revs other than A the size of WAKEUPINTTHR is 25 bits.
FTDF_SET_FIELD(ALWAYS_ON_REGMAP_WAKEUPINTTHR, wakeUpIntThr);
FTDF_SET_FIELD(ALWAYS_ON_REGMAP_WAKEUPENABLE, 1);
#endif
FTDF_exitCritical();
return FTDF_TRUE;
}
void FTDF_wakeUp(void)
{
#ifndef FTDF_PHY_API
FTDF_criticalVar();
FTDF_enterCritical();
FTDF_SET_FIELD(ALWAYS_ON_REGMAP_WAKEUPENABLE, 0);
#if !defined(FTDF_NO_CSL) || !defined(FTDF_NO_TSCH)
// Capture the current value of both the event generator and the timestamp generator
// and phase on the rising edge of LP_CLK
FTDF_SET_FIELD(ON_OFF_REGMAP_GETGENERATORVAL, 1);
volatile uint32_t *getGeneratorValE = FTDF_GET_FIELD_ADDR(ON_OFF_REGMAP_GETGENERATORVAL_E);
// Wait until data is ready
while ((*getGeneratorValE & MSK_F_FTDF_ON_OFF_REGMAP_GETGENERATORVAL_E) == 0)
{ }
uint32_t eventNewCurrVal = FTDF_GET_FIELD(ON_OFF_REGMAP_EVENTCURRVAL);
#ifdef SIMULATOR
*getGeneratorValE &= ~MSK_F_FTDF_ON_OFF_REGMAP_GETGENERATORVAL_E;
#else
*getGeneratorValE = MSK_F_FTDF_ON_OFF_REGMAP_GETGENERATORVAL_E;
#endif
// Backward calculate the time slept
//FTDF_PSec sleepTime = ( (uint64_t)( eventNewCurrVal - FTDF_eventCurrVal ) * FTDF_lowPowerClockCycle ) + FTDF_wakeUpLatency;
FTDF_PSec sleepTime;
if (eventNewCurrVal >= FTDF_eventCurrVal) /* Check for wraps. */
{
sleepTime = (eventNewCurrVal - FTDF_eventCurrVal) * FTDF_lowPowerClockCycle +
FTDF_wakeUpLatency;
}
else
{
sleepTime = (eventNewCurrVal +
(MSK_R_FTDF_ON_OFF_REGMAP_EVENTCURRVAL - FTDF_eventCurrVal)) *
FTDF_lowPowerClockCycle + FTDF_wakeUpLatency;
}
// Calculate sync values
uint64_t newSyncVals = ((uint64_t)FTDF_timeStampCurrVal << 8) | (FTDF_timeStampCurrPhaseVal & 0xff);
newSyncVals += (sleepTime / 62500) + 1;
uint32_t syncTimestampThr = FTDF_eventCurrVal + (sleepTime / FTDF_lowPowerClockCycle);
uint32_t syncTimestampVal = (newSyncVals & 0xffffffff00) >> 8;
uint32_t syncTimestampPhaseVal = (newSyncVals & 0xff);
// Set values
FTDF_SET_FIELD(ON_OFF_REGMAP_SYNCTIMESTAMPTHR, syncTimestampThr);
FTDF_SET_FIELD(ON_OFF_REGMAP_SYNCTIMESTAMPVAL, syncTimestampVal);
FTDF_SET_FIELD(ON_OFF_REGMAP_SYNCTIMESTAMPPHASEVAL, syncTimestampPhaseVal);
FTDF_SET_FIELD(ON_OFF_REGMAP_SYNCTIMESTAMPENA, 1);
#endif
FTDF_exitCritical();
#ifndef FTDF_NO_CSL
FTDF_wakeUpEnableLe = FTDF_pib.leEnabled;
FTDF_pib.leEnabled = FTDF_FALSE;
#endif /* FTDF_NO_CSL */
#ifndef FTDF_NO_TSCH
FTDF_wakeUpEnableTsch = FTDF_pib.tschEnabled;
FTDF_pib.tschEnabled = FTDF_FALSE;
#endif /* FTDF_NO_TSCH */
#endif /* ! FTDF_PHY_API */
// Init LMAC
FTDF_initLmac();
}