/* * * 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(chip_with_low_power) && (chip_with_low_power == 1) #include #include #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" 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; } 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