blob: eb980e179882b1fa2f3d550852f5a94160aa285d [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* 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.
*/
/**
* @file
* Provides a glue layer between Matter and NXP-SDK Low Power
*/
#if defined(cPWR_UsePowerDownMode) && (cPWR_UsePowerDownMode)
#include <platform/CHIPDeviceLayer.h>
#include <platform/ThreadStackManager.h>
#include "Keyboard.h"
#include "MacSched.h"
#include "OtaSupport.h"
#include "PWR_Configuration.h"
#include "PWR_Interface.h"
#include "RNG_Interface.h"
#include "app_dual_mode_low_power.h"
#include "app_dual_mode_switch.h"
#include "k32w0-chip-mbedtls-config.h"
#include <AppTask.h> // nogncheck
#include "app_config.h"
using namespace ::chip;
using namespace ::chip::Inet;
using namespace ::chip::DeviceLayer;
extern "C" void InitLowPower();
extern "C" void vMMAC_IntHandlerBbc();
extern "C" void vMMAC_IntHandlerPhy();
extern "C" void BOARD_SetClockForPowerMode(void);
extern "C" void stopM2();
extern "C" void sched_enable();
extern "C" uint64_t otPlatTimeGet(void);
extern "C" void vOptimizeConsumption(void);
WEAK void dm_switch_wakeupCallBack(void);
WEAK void dm_switch_preSleepCallBack(void);
WEAK void vOptimizeConsumption(void);
static void ThreadExitSleep();
static void BOARD_SetClockForWakeup(void);
typedef struct
{
bool_t threadInitialized;
uint32_t threadWarmBootInitTime;
} sDualModeAppStates;
typedef void * appCallbackParam_t;
typedef void (*appCallbackHandler_t)(appCallbackParam_t param);
static sDualModeAppStates dualModeStates;
/* 15.4 warm time must be > 0, so this value will be
* updated when 15.4 is initialized for the first time
*/
constexpr uint16_t kThreadWarmNotInitializedValue = 1000; /* 1 ms */
extern "C" void otTaskletsSignalPending(otInstance * p_instance);
#define ENABLE_LOW_POWER_LOGS 0
void InitLowPower()
{
PWR_Init();
/* Internal - MATTER-303: keep in retention the entire RAM1 for the moment */
PWR_vAddRamRetention((uint32_t) 0x4020000, 0x10000);
PWR_RegisterLowPowerExitCallback(dm_switch_wakeupCallBack);
PWR_RegisterLowPowerEnterCallback(dm_switch_preSleepCallBack);
dualModeStates.threadWarmBootInitTime = kThreadWarmNotInitializedValue;
dm_lp_init();
}
extern "C" void setThreadInitialized(bool isInitialized)
{
dualModeStates.threadInitialized = isInitialized;
}
extern "C" bool isThreadInitialized()
{
return dualModeStates.threadInitialized;
}
uint32_t dm_switch_get15_4InitWakeUpTime(void)
{
return dualModeStates.threadWarmBootInitTime;
}
extern "C" bleResult_t App_PostCallbackMessage(appCallbackHandler_t handler, appCallbackParam_t param)
{
AppEvent event;
event.Type = AppEvent::kEventType_Lp;
event.Handler = handler;
event.param = param;
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("App_PostCallbackMessage %d", (uint32_t) param);
#endif
GetAppTask().PostEvent(&event);
return gBleSuccess_c;
}
WEAK void dm_switch_wakeupCallBack(void)
{
BOARD_SetClockForWakeup();
SHA_ClkInit(SHA_INSTANCE);
CLOCK_EnableClock(kCLOCK_Aes);
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("dm_switch_wakeupCallBack");
K32W_LOG("Warm up time actual value: %d", dualModeStates.threadWarmBootInitTime);
#endif
RNG_Init();
SecLib_Init();
OTA_InitExternalMemory();
KBD_PrepareExitLowPower();
#if gAdcUsed_d
BOARD_ADCWakeupInit();
#endif
PWR_WakeupReason_t wakeReason = PWR_GetWakeupReason();
if (wakeReason.Bits.FromBLE_LLTimer == 1)
{
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("woken up from LL");
#endif
}
else if (wakeReason.Bits.FromKeyBoard == 1)
{
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("woken up from FromKeyBoard");
#endif
}
else if (wakeReason.Bits.FromTMR == 1)
{
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("woken up from TMR");
#endif
}
else if (wakeReason.Bits.FromWakeTimer == 1)
{
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("woken up from Wake Timer");
#endif
}
else
{
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("woken up from unknown source");
#endif
}
dm_lp_wakeup();
}
WEAK void vOptimizeConsumption(void)
{
/* Intentionally left empty, user needs to redefine it at application level */
}
WEAK void dm_switch_preSleepCallBack(void)
{
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("dm_switch_preSleepCallBack");
#endif
/* Inform the low power dual mode module that we will sleep.
It disables the MAC scheduler.
Disabling the scheduler must be done before calling vMMAC_Disable()
so it works correctly */
dm_lp_preSleep();
if (dualModeStates.threadInitialized)
{
otPlatRadioDisable(NULL);
setThreadInitialized(FALSE);
}
EEPROM_DeInit();
vOptimizeConsumption();
/* disable SHA clock */
SHA_ClkDeinit(SHA_INSTANCE);
/* disable the AES clock */
CLOCK_DisableClock(kCLOCK_Aes);
/* configure pins for power down mode */
BOARD_SetPinsForPowerMode();
/* DeInitialize application support for drivers */
BOARD_DeInitAdc();
/* DeInit the necessary clocks */
BOARD_SetClockForPowerMode();
}
void dm_switch_init15_4AfterWakeUp(void)
{
uint64_t tick1 = 0;
if (dualModeStates.threadWarmBootInitTime == kThreadWarmNotInitializedValue)
{
/* Get a 32K tick */
PWR_Start32kCounter();
tick1 = otPlatTimeGet();
}
OSA_InstallIntHandler(ZIGBEE_MAC_IRQn, vMMAC_IntHandlerBbc);
OSA_InstallIntHandler(ZIGBEE_MODEM_IRQn, vMMAC_IntHandlerPhy);
/* Radio must be re-enabled after waking up from sleep.
The module is completely disabled in power down mode.
15.4 is set as active by default */
otPlatRadioEnable(NULL);
/* set the correct state */
vDynRequestState(E_DYN_SLAVE, E_DYN_STATE_INACTIVE); /* 15.4 */
vDynRequestState(E_DYN_MASTER, E_DYN_STATE_ACTIVE); /* BLE */
sched_enable();
if (dualModeStates.threadWarmBootInitTime == kThreadWarmNotInitializedValue)
{
dualModeStates.threadWarmBootInitTime = (uint32_t)(otPlatTimeGet() - tick1);
/* Add a margin of 0.5 ms */
dualModeStates.threadWarmBootInitTime += 500;
#if ENABLE_LOW_POWER_LOGS
K32W_LOG("Calibration: %d", dualModeStates.threadWarmBootInitTime);
#endif
}
setThreadInitialized(TRUE);
/* wake up the Thread stack and check if any processing needs to be done.
We're called form ISR context */
otSysEventSignalPending();
}
extern "C" void App_NotifyWakeup(void)
{
if ((!dualModeStates.threadInitialized))
{
/* Notify the dual mode low power mode */
dm_lp_processEvent((void *) e15_4WakeUpEnded);
}
}
extern "C" void App_AllowDeviceToSleep()
{
PWR_AllowDeviceToSleep();
}
extern "C" void App_DisallowDeviceToSleep()
{
PWR_DisallowDeviceToSleep();
}
static void BOARD_SetClockForWakeup(void)
{
/* Enables the clock for the I/O controller block. 0: Disable. 1: Enable.: 0x01u */
CLOCK_EnableClock(kCLOCK_Iocon);
/* Enables the clock for the GPIO0 module */
CLOCK_EnableClock(kCLOCK_Gpio0);
}
#endif