对比新文件 |
| | |
| | | /* |
| | | * Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and |
| | | * its subsidiaries and affiliates (collectly called MKSEMI). |
| | | * |
| | | * 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, except as embedded into an MKSEMI |
| | | * integrated circuit in a product or a software update for such product, |
| | | * 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 MKSEMI nor the names of its contributors may be used |
| | | * to endorse or promote products derived from this software without |
| | | * specific prior written permission. |
| | | * |
| | | * 4. This software, with or without modification, must only be used with a |
| | | * MKSEMI integrated circuit. |
| | | * |
| | | * 5. Any software provided in binary form under this license must not be |
| | | * reverse engineered, decompiled, modified and/or disassembled. |
| | | * |
| | | * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED |
| | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| | | * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| | | * DISCLAIMED. IN NO EVENT SHALL MKSEMI 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 "mk_power.h" |
| | | #include "mk_trace.h" |
| | | #include "mk_clock.h" |
| | | #include "mk_io.h" |
| | | #include "mk_flash.h" |
| | | #include "mk_uart.h" |
| | | #include "mk_gpio.h" |
| | | #include "mk_reset.h" |
| | | #include "mk_misc.h" |
| | | #ifdef WSF_EN |
| | | #include "wsf_timer.h" |
| | | #endif |
| | | #ifdef UWB_EN |
| | | #include "mk_uwb.h" |
| | | #include "mk_sleep_timer.h" |
| | | #endif |
| | | |
| | | /* UCI SPI full-duplex handshake interface */ |
| | | #ifndef UCI_INTF_SPI_FD_HS |
| | | #define UCI_INTF_SPI_FD_HS (0) |
| | | #endif |
| | | |
| | | /* UCI SPI half-duplex handshake interface */ |
| | | #ifndef UCI_INTF_SPI_HD_HS |
| | | #define UCI_INTF_SPI_HD_HS (0) |
| | | #endif |
| | | |
| | | #if (UCI_INTF_SPI_FD_HS || UCI_INTF_SPI_HD_HS) |
| | | #include "mk_spi.h" |
| | | #endif |
| | | |
| | | #ifndef I2C_CHECK_EN |
| | | #define I2C_CHECK_EN (0) |
| | | #endif |
| | | |
| | | #if I2C_CHECK_EN |
| | | #include "mk_i2c.h" |
| | | #endif |
| | | |
| | | #ifndef LOW_POWER_CLOCK_PPM |
| | | #define LOW_POWER_CLOCK_PPM (50) |
| | | #endif |
| | | |
| | | #ifndef SYS_CLK_SOURCE |
| | | #define SYS_CLK_SOURCE CLOCK_62P4M_XTAL38P4M_TO_SYS_CLK |
| | | #endif |
| | | |
| | | // Including 38.4MHz clock PLL ready time and application recovery time, unit: us |
| | | #define RECOVERY_TIME_FROM_LOW_POWER (1000) |
| | | |
| | | // Considering 32KHz clock ppm and sleep time, x = sleep time (us), the result should < PHY_SLEEP_TIME_US_MIN |
| | | #define WAKEUP_IN_ADVANCE_TIME(x) (RECOVERY_TIME_FROM_LOW_POWER + (x)*LOW_POWER_CLOCK_PPM / 1000000) |
| | | |
| | | #ifdef UWB_EN |
| | | extern uint32_t slp_cnt; |
| | | uint32_t slp_cnt = 0; |
| | | #endif |
| | | |
| | | extern void board_prepare_for_power_down(void); |
| | | extern void board_restore_from_power_down(void); |
| | | extern void app_restore_from_power_down(void); |
| | | |
| | | extern void power_fem_tx_ctrl(uint8_t on_off); |
| | | extern void power_fem_rx_ctrl(uint8_t on_off); |
| | | |
| | | // on_off: 1 - ON, 0 - OFF |
| | | __WEAK void power_fem_tx_ctrl(uint8_t on_off) |
| | | { |
| | | } |
| | | |
| | | // on_off: 1 - ON, 0 - OFF |
| | | __WEAK void power_fem_rx_ctrl(uint8_t on_off) |
| | | { |
| | | } |
| | | |
| | | static struct POWER_REQUEST_T power_env = { |
| | | .power_request[POWER_MODE_ACTIVE] = 0, |
| | | .power_request[POWER_MODE_SLEEP] = 0, |
| | | .power_request[POWER_MODE_POWER_DOWN] = 0, |
| | | .power_request[POWER_MODE_DEEP_POWER_DOWN] = 0, |
| | | .power_request[POWER_MODE_SHELF] = 0, |
| | | }; |
| | | |
| | | void power_init(void) |
| | | { |
| | | if (SYSCON->SYS_CMU & SYSCON_SYS_CMU_32K_CLK_SEL_MSK) |
| | | { |
| | | // enable PMU, disable rco 32k |
| | | SYSCON->PMU_CTRL0 |= (1U << 31) | (1 << 5); |
| | | } |
| | | else |
| | | { |
| | | // enable PMU, disable Xtal32k |
| | | SYSCON->PMU_CTRL0 |= (1U << 31) | (1 << 7); |
| | | } |
| | | |
| | | // DG REF from LP_BG |
| | | SYSCON->REG_DIG &= ~(1U << 3); |
| | | |
| | | // fix high temperature wakeup fail issue |
| | | SYSCON->IVREF_ULP = 0x20000; |
| | | |
| | | #if DCDC_EN |
| | | // DC-DC enable |
| | | SYSCON->SYS_CTRL &= ~(3U << 18); |
| | | #endif |
| | | |
| | | bor_close(); |
| | | } |
| | | |
| | | void power_on_radio(uint8_t tx_en, uint8_t rx_en) |
| | | { |
| | | // board_led_on(BOARD_LED_1); |
| | | uint32_t val; |
| | | // HW gate clock |
| | | #if 0 |
| | | // Clock on - TX | RX |
| | | val = SYSCON->SYS_CMU; |
| | | val = tx_en ? (val | (1U << CLOCK_TX)) : val; |
| | | val = rx_en ? (val | (1U << CLOCK_RX)) : val; |
| | | SYSCON->SYS_CMU = val; |
| | | #endif |
| | | |
| | | val = REG_READ(0x40000400); |
| | | // Radio on |
| | | REG_WRITE(0x40000400, val | 0x30000000); |
| | | power_mode_request(POWER_UNIT_RF, POWER_MODE_SLEEP); |
| | | |
| | | if (tx_en) |
| | | { |
| | | power_fem_tx_ctrl(1); |
| | | } |
| | | if (rx_en) |
| | | { |
| | | power_fem_rx_ctrl(1); |
| | | } |
| | | // board_led_off(BOARD_LED_1); |
| | | // LOG_INFO(TRACE_MODULE_DRIVER, "power on radio %x\r\n", SYSCON->SYS_CMU); |
| | | } |
| | | |
| | | void power_off_radio(void) |
| | | { |
| | | // board_led_on(BOARD_LED_2); |
| | | // Radio off |
| | | uint32_t val = REG_READ(0x40000400); |
| | | val &= ~0x30000000U; |
| | | REG_WRITE(0x40000400, val | 0x20000000); |
| | | |
| | | #if 0 |
| | | // Clock off - TX | RX |
| | | SYSCON->SYS_CMU &= ~((1U << CLOCK_RX) | (1U << CLOCK_TX)); |
| | | #endif |
| | | |
| | | power_fem_tx_ctrl(0); |
| | | power_fem_rx_ctrl(0); |
| | | power_mode_clear(POWER_UNIT_RF); |
| | | // board_led_off(BOARD_LED_2); |
| | | // LOG_INFO(TRACE_MODULE_DRIVER, "power off radio\r\n"); |
| | | } |
| | | |
| | | void power_enter_sleep_mode(void) |
| | | { |
| | | // board_led_off(BOARD_LED_1); |
| | | // |
| | | // WFI SLEEP |
| | | // |
| | | // LOG_INFO(TRACE_MODULE_DRIVER, "WFI SLEEP\r\n"); |
| | | |
| | | // Ensure we SLEEP - SLEEPDEEP should be clear |
| | | // SCR[2] = 0 |
| | | SCB->SCR &= ~(1UL << 2); |
| | | |
| | | // Wait For Interrupt |
| | | __WFI(); |
| | | // board_led_on(BOARD_LED_1); |
| | | } |
| | | |
| | | #ifdef SWD_WAKEUP_EN |
| | | static void power_swd_wakeup_en(void) |
| | | { |
| | | // if ((REG_READ(0xE000EDF0) & 0x1) == 0) |
| | | { |
| | | /* Switch function of IO_16 from SWDIO to GPIO, the debugger can wakeup chip */ |
| | | gpio_pin_set_dir(IO_PIN_16, GPIO_DIR_IN, 0); |
| | | io_pull_set(IO_PIN_16, IO_PULL_UP, IO_PULL_UP_LEVEL0); |
| | | io_pin_mux_set(IO_PIN_16, IO_FUNC1); |
| | | |
| | | /* Read IO_16 and set the opposite level as wakeup level */ |
| | | power_wakeup_enable(POWER_WAKEUP_BY_GPIO_16, (GPIO->DATA & (1 << IO_PIN_16)) ? POWER_WAKEUP_LEVEL_LOW : POWER_WAKEUP_LEVEL_HIGH); |
| | | |
| | | /* disable swd */ |
| | | } |
| | | } |
| | | |
| | | static void power_swd_restore(void) |
| | | { |
| | | // if ((REG_READ(0xE000EDF0) & 0x1) == 0) |
| | | { |
| | | /* Disable IO_16 wakeup */ |
| | | power_wakeup_disable(POWER_WAKEUP_BY_GPIO_16); |
| | | |
| | | /* Restore IO_16 as SWDIO */ |
| | | io_pin_mux_set(IO_PIN_16, IO_FUNC0); |
| | | |
| | | /* re-enable swd */ |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | void RAM_FUNC enter_power_down_in_ram(void); |
| | | void enter_power_down_in_ram(void) |
| | | { |
| | | uint32_t wakeup_en = 0; |
| | | uint32_t wakeup_lvl = 0; |
| | | |
| | | SYSCON->PMU_CTRL1 &= ~(1U << 16); |
| | | |
| | | // (REFPLL | RXLDO | TXLDO | DREG) - OFF |
| | | SYSCON->PMU_CTRL0 |= (1U << 10) | (1U << 11) | (1U << 12) | 0x1; |
| | | |
| | | /* Enable IO wakeup */ |
| | | NVIC_ClearPendingIRQ(WAKEUP_IRQn); |
| | | NVIC_EnableIRQ(WAKEUP_IRQn); |
| | | |
| | | // flash power down - by flash_close() |
| | | flash_power_down(FLASH_ID0); |
| | | |
| | | uint8_t val = REG_READ_BYTE(EFUSE_SHADOW_BASE + 0x67); |
| | | if ((val & 0x80) == 0) |
| | | { |
| | | // switch to external flash io |
| | | REG_WRITE_BYTE(EFUSE_SHADOW_BASE + 0x67, val | 0x80); |
| | | } |
| | | |
| | | wakeup_en = SYSCON->WAKEUP_EN & SYSCON_WAKEUP_EN_IO_MSK; |
| | | wakeup_lvl = SYSCON->WAKEUP_POL; |
| | | |
| | | #ifdef SWD_WAKEUP_EN |
| | | power_swd_wakeup_en(); |
| | | #endif |
| | | |
| | | // Latch IO |
| | | SYSCON->IO_SLP_OUT = GPIO->DATAOUT; |
| | | SYSCON->IO_SLP_OE = GPIO->OUTENSET; |
| | | SYSCON->IO_SLP_EI = SYSCON->IO_EI; |
| | | SYSCON->IO_SLP_PDN = SYSCON->IO_PDN; |
| | | SYSCON->IO_SLP_PUP[0] = SYSCON->IO_PUP[0]; |
| | | SYSCON->IO_SLP_PUP[1] = SYSCON->IO_PUP[1]; |
| | | SYSCON->IO_SLP_PUP[2] = SYSCON->IO_PUP[2]; |
| | | |
| | | // reduce DG current 60% |
| | | SYSCON->CAP_DIV_CFG = 0x0; |
| | | |
| | | // Clock off - CALIB (retention) |
| | | SYSCON->SYS_CMU &= ~(1U << CLOCK_CALIB); |
| | | |
| | | SYSCON->PMU_CTRL1 &= ~(1U << 17); |
| | | |
| | | // Wait For Interrupt |
| | | __WFI(); |
| | | |
| | | SYSCON->PMU_CTRL1 |= (1U << 17); |
| | | |
| | | // Clock on - CALIB |
| | | SYSCON->SYS_CMU |= (1U << CLOCK_CALIB); |
| | | |
| | | if ((val & 0x80) == 0) |
| | | { |
| | | // switch to internal flash io |
| | | REG_WRITE_BYTE(EFUSE_SHADOW_BASE + 0x67, val & 0x7f); |
| | | } |
| | | |
| | | SYSCON->CAP_DIV_CFG = 0x7; |
| | | |
| | | // flash power up |
| | | flash_power_up(FLASH_ID0); |
| | | |
| | | #ifdef XIP_EN |
| | | flash_open_for_xip(FLASH_ID0); |
| | | #endif |
| | | NVIC_DisableIRQ(WAKEUP_IRQn); |
| | | NVIC_ClearPendingIRQ(WAKEUP_IRQn); |
| | | |
| | | // Restore IO |
| | | GPIO->DATAOUT = SYSCON->IO_SLP_OUT; |
| | | GPIO->OUTENSET = SYSCON->IO_SLP_OE; |
| | | |
| | | #ifdef SWD_WAKEUP_EN |
| | | // power_swd_restore(); |
| | | #endif |
| | | |
| | | if (wakeup_en) |
| | | { |
| | | GPIO->INTTYPECLR = wakeup_en; |
| | | GPIO->INTPOLCLR = wakeup_en & wakeup_lvl; |
| | | GPIO->INTPOLSET = wakeup_en & ~wakeup_lvl; |
| | | GPIO->INTENSET = wakeup_en; |
| | | } |
| | | |
| | | #if DCDC_EN |
| | | // BUCK ready <50us |
| | | while ((SYSCON->CLK_STATUS & 0x20) == 0) |
| | | { |
| | | } |
| | | delay_us(10); |
| | | #endif |
| | | |
| | | // RXLDO - ON |
| | | SYSCON->PMU_CTRL0 &= ~(1U << 11); |
| | | delay_us(10); |
| | | |
| | | // TXLDO - ON |
| | | SYSCON->PMU_CTRL0 &= ~(1U << 12); |
| | | delay_us(10); |
| | | |
| | | // DREG - ON |
| | | SYSCON->PMU_CTRL0 &= ~(0x1U); |
| | | |
| | | // DBB ready <50us |
| | | while ((SYSCON->SYSTEM_STATUS & (1U << 20)) == 0) |
| | | { |
| | | } |
| | | SYSCON->PMU_CTRL1 |= (1U << 16); |
| | | } |
| | | |
| | | void power_enter_power_down_mode(bool deep_en) |
| | | { |
| | | // |
| | | // WFI SLEEPDEEP |
| | | // |
| | | |
| | | // LOG_INFO(TRACE_MODULE_DRIVER, "WFI SLEEPDEEP\r\n"); |
| | | |
| | | board_prepare_for_power_down(); |
| | | |
| | | #ifdef UWB_EN |
| | | // TODO: store uwb confiuguration |
| | | struct KEY_T mac_ccm_key; |
| | | mac_ccm_key.KEY_W0 = REG_READ(0x5000A080); |
| | | mac_ccm_key.KEY_W1 = REG_READ(0x5000A084); |
| | | mac_ccm_key.KEY_W2 = REG_READ(0x5000A088); |
| | | mac_ccm_key.KEY_W3 = REG_READ(0x5000A08C); |
| | | phy_timer_pause(); |
| | | #endif |
| | | |
| | | #if SYS_TICK_EN |
| | | sys_tick_pause(); |
| | | #endif |
| | | |
| | | // Ensure we SLEEPDEEP - SLEEPDEEP should be set |
| | | // SCR[2] = 1 |
| | | SCB->SCR |= (1U << 2); |
| | | |
| | | if (deep_en) |
| | | { |
| | | // power off 32K in deep power down |
| | | SYSCON->PMU_CTRL1 |= (1U << 7) | (1U << 5); |
| | | } |
| | | else |
| | | { |
| | | // keep 32k power on in power down mode |
| | | SYSCON->PMU_CTRL1 &= ~((1U << 7) | (1U << 5)); |
| | | } |
| | | |
| | | // switch system clock from XTAL to RO |
| | | clock_attach(CLOCK_48M_RO_TO_SYS_CLK); |
| | | |
| | | // for XIP |
| | | enter_power_down_in_ram(); |
| | | |
| | | #ifdef UWB_EN |
| | | // TODO: restore uwb configuration |
| | | phy_restore(NULL); |
| | | mac_restart(); |
| | | mac_update_ccm_key((uint32_t *)&mac_ccm_key); |
| | | #endif |
| | | |
| | | // crystal ready >400us |
| | | while ((SYSCON->CLK_STATUS & 0x1) == 0) |
| | | { |
| | | } |
| | | // REFPLL - ON |
| | | SYSCON->PMU_CTRL0 &= ~(1U << 10); |
| | | |
| | | // switch system clock from RO to XTAL |
| | | clock_attach(SYS_CLK_SOURCE); |
| | | |
| | | #ifdef UWB_EN |
| | | slp_cnt = phy_timer_resume(); |
| | | #endif |
| | | |
| | | #if SYS_TICK_EN |
| | | sys_tick_resume(); |
| | | #endif |
| | | |
| | | board_restore_from_power_down(); |
| | | } |
| | | |
| | | void RAM_FUNC enter_shelf_mode_in_ram(uint8_t key, uint32_t cmd); |
| | | |
| | | #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100) |
| | | #pragma clang optimize off |
| | | #elif defined(__GNUC__) |
| | | #pragma GCC diagnostic push |
| | | #pragma GCC optimize("O0") |
| | | #else |
| | | #pragma optimize = none |
| | | #endif |
| | | |
| | | void enter_shelf_mode_in_ram(uint8_t key, uint32_t cmd) |
| | | { |
| | | // flash power down - by flash_close() |
| | | flash_power_down(FLASH_ID0); |
| | | |
| | | SYSCON->SHELF_KEY = key; |
| | | SYSCON->PMU_CTRL1 = cmd; |
| | | } |
| | | |
| | | #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100) |
| | | #pragma clang optimize on |
| | | #elif defined(__GNUC__) |
| | | #pragma GCC diagnostic pop |
| | | #endif |
| | | |
| | | void power_enter_shelf_mode(void) |
| | | { |
| | | board_prepare_for_power_down(); |
| | | enter_shelf_mode_in_ram(0xD5, 0x80031FCB); |
| | | } |
| | | |
| | | void power_mode_request(enum POWER_REQ_UNIT_T item, enum POWER_MODE_T mode) |
| | | { |
| | | uint32_t lock = int_lock(); |
| | | power_env.power_request[POWER_MODE_ACTIVE] &= ~item; |
| | | power_env.power_request[POWER_MODE_SLEEP] &= ~item; |
| | | power_env.power_request[POWER_MODE_POWER_DOWN] &= ~item; |
| | | power_env.power_request[POWER_MODE_DEEP_POWER_DOWN] &= ~item; |
| | | power_env.power_request[POWER_MODE_SHELF] &= ~item; |
| | | power_env.power_request[mode] |= item; |
| | | int_unlock(lock); |
| | | } |
| | | |
| | | void power_mode_clear(enum POWER_REQ_UNIT_T item) |
| | | { |
| | | uint32_t lock = int_lock(); |
| | | power_env.power_request[POWER_MODE_ACTIVE] &= ~item; |
| | | power_env.power_request[POWER_MODE_SLEEP] &= ~item; |
| | | power_env.power_request[POWER_MODE_POWER_DOWN] &= ~item; |
| | | power_env.power_request[POWER_MODE_DEEP_POWER_DOWN] &= ~item; |
| | | power_env.power_request[POWER_MODE_SHELF] &= ~item; |
| | | int_unlock(lock); |
| | | } |
| | | |
| | | uint32_t power_mode_requester_get(enum POWER_MODE_T mode) |
| | | { |
| | | return power_env.power_request[mode]; |
| | | } |
| | | |
| | | void power_wakeup_enable(enum POWER_WAKEUP_SOURCE_T src, enum POWER_WAKEUP_POLARITY_T pol) |
| | | { |
| | | if (pol == POWER_WAKEUP_LEVEL_HIGH) |
| | | { |
| | | // high wakeup |
| | | SYSCON->WAKEUP_POL &= ~(1 << src); |
| | | } |
| | | else if (pol == POWER_WAKEUP_LEVEL_LOW) |
| | | { |
| | | // low wakeup |
| | | SYSCON->WAKEUP_POL |= (1 << src); |
| | | } |
| | | SYSCON->WAKEUP_EN |= (1 << src) | SYSCON_WAKEUP_EN_MSK; |
| | | } |
| | | |
| | | void power_wakeup_disable(enum POWER_WAKEUP_SOURCE_T src) |
| | | { |
| | | SYSCON->WAKEUP_EN &= ~(1 << src); |
| | | if (SYSCON->WAKEUP_EN == SYSCON_WAKEUP_EN_MSK) |
| | | { |
| | | SYSCON->WAKEUP_EN = 0; |
| | | } |
| | | } |
| | | |
| | | static void power_check_io_power_mode(void) |
| | | { |
| | | if (power_env.power_request[POWER_MODE_ACTIVE] & ~(uint32_t)(POWER_UNIT_GPIO)) |
| | | { |
| | | // stay active |
| | | return; |
| | | } |
| | | |
| | | if (power_env.power_request[POWER_MODE_SLEEP]) |
| | | { |
| | | // clear gpio active request |
| | | power_mode_clear(POWER_UNIT_GPIO); |
| | | return; |
| | | } |
| | | |
| | | // check GPIO clock |
| | | if ((SYSCON->SYS_CMU & (1 << CLOCK_GPIO)) == 0) |
| | | { |
| | | // clear gpio active request |
| | | power_mode_clear(POWER_UNIT_GPIO); |
| | | return; |
| | | } |
| | | |
| | | uint32_t wake_en = SYSCON->WAKEUP_EN & SYSCON_WAKEUP_EN_IO_MSK; |
| | | uint32_t wakeup_level = SYSCON->WAKEUP_POL; |
| | | uint32_t real_level = GPIO->DATA; |
| | | |
| | | if ((wakeup_level ^ real_level) & wake_en) |
| | | { |
| | | // IO wakeup source exist - stay active |
| | | power_mode_request(POWER_UNIT_GPIO, POWER_MODE_ACTIVE); |
| | | } |
| | | else |
| | | { |
| | | // clear gpio active request |
| | | power_mode_clear(POWER_UNIT_GPIO); |
| | | } |
| | | } |
| | | |
| | | static void power_check_if_power_mode(void) |
| | | { |
| | | // check UART power mode |
| | | if ((uart_state_get(UART_ID0) & UART_STATE_BUSY_TX_RX) || (uart_state_get(UART_ID1) & UART_STATE_BUSY_TX_RX)) |
| | | { |
| | | power_mode_request(POWER_UNIT_UART, POWER_MODE_SLEEP); |
| | | } |
| | | else if (uart_fifo_busy(UART_ID0) || uart_fifo_busy(UART_ID1)) |
| | | { |
| | | power_mode_request(POWER_UNIT_UART, POWER_MODE_ACTIVE); |
| | | } |
| | | |
| | | #if I2C_CHECK_EN |
| | | // check I2C power mode |
| | | if (i2c_state_get(I2C_ID0) & I2C_STATE_BUSY_TX_RX) |
| | | { |
| | | power_mode_request(POWER_UNIT_I2C, POWER_MODE_SLEEP); |
| | | } |
| | | #endif |
| | | } |
| | | |
| | | static void power_clear_if_power_mode(void) |
| | | { |
| | | power_mode_clear(POWER_UNIT_UART); |
| | | |
| | | #if (UCI_INTF_SPI_FD_HS || UCI_INTF_SPI_HD_HS) |
| | | power_mode_clear(POWER_UNIT_SPI); |
| | | #endif |
| | | |
| | | #if I2C_CHECK_EN |
| | | power_mode_clear(POWER_UNIT_I2C); |
| | | #endif |
| | | } |
| | | |
| | | #ifdef UWB_EN |
| | | static void power_check_uwb_power_mode(void) |
| | | { |
| | | enum POWER_MODE_T pm; |
| | | // check os timer |
| | | uint32_t os_time_ms = PHY_SLEEP_TIME_MS_MAX; |
| | | uint32_t phy_time_us = PHY_SLEEP_TIME_MS_MAX * 1000; |
| | | |
| | | #ifdef WSF_EN |
| | | if (wsfOsReadyToSleep()) |
| | | { |
| | | pm = WsfTimerSleepCheck(&os_time_ms); |
| | | |
| | | if (pm) |
| | | { |
| | | #endif |
| | | uint8_t busy = mac_is_busy(); |
| | | if (busy) |
| | | { |
| | | pm = POWER_MODE_SLEEP; |
| | | } |
| | | else |
| | | { |
| | | if (phy_timer_is_programmed()) |
| | | { |
| | | // check phy timer |
| | | uint32_t phy_time_tick = phy_timer_count_left(); |
| | | phy_time_us = PHY_TIMER_COUNT_TO_US(phy_time_tick); |
| | | } |
| | | |
| | | // sleep time |
| | | uint32_t sleep_time_us = MIN((uint32_t)(os_time_ms * 1000), (uint32_t)phy_time_us); |
| | | |
| | | if (sleep_time_us > PHY_SLEEP_TIME_US_MIN) |
| | | { |
| | | pm = POWER_MODE_POWER_DOWN; |
| | | // make sure wakeup in advance, need to correct by 32K ppm |
| | | sleep_time_us -= WAKEUP_IN_ADVANCE_TIME(sleep_time_us); |
| | | |
| | | uint32_t sleep_time_tick = __US_TO_32K_CNT(sleep_time_us); |
| | | |
| | | sleep_timer_start(sleep_time_tick); |
| | | } |
| | | else |
| | | { |
| | | pm = POWER_MODE_SLEEP; |
| | | } |
| | | } |
| | | #ifdef WSF_EN |
| | | } |
| | | } |
| | | else |
| | | { |
| | | pm = POWER_MODE_ACTIVE; |
| | | } |
| | | #endif |
| | | power_mode_request(POWER_UNIT_UWB, pm); |
| | | } |
| | | #endif |
| | | |
| | | void power_manage(void) |
| | | { |
| | | uint32_t lock = int_lock(); |
| | | uint8_t wakeup_from_power_down = 0; |
| | | |
| | | // check GPIO wakeup source |
| | | power_check_io_power_mode(); |
| | | |
| | | if (power_env.power_request[POWER_MODE_ACTIVE]) |
| | | { |
| | | // stay in active mode |
| | | } |
| | | else |
| | | { |
| | | #ifdef UWB_EN |
| | | power_check_uwb_power_mode(); |
| | | #endif |
| | | power_check_if_power_mode(); |
| | | |
| | | if (power_env.power_request[POWER_MODE_ACTIVE]) |
| | | { |
| | | // stay in active mode |
| | | } |
| | | else if (power_env.power_request[POWER_MODE_SLEEP]) |
| | | { |
| | | // enter sleep mode |
| | | power_enter_sleep_mode(); |
| | | } |
| | | else if (power_env.power_request[POWER_MODE_POWER_DOWN]) |
| | | { |
| | | // enter power down mode |
| | | // board_led_on(BOARD_LED_1); |
| | | power_enter_power_down_mode(0); |
| | | // board_led_off(BOARD_LED_1); |
| | | wakeup_from_power_down = 1; |
| | | } |
| | | else if (power_env.power_request[POWER_MODE_DEEP_POWER_DOWN]) |
| | | { |
| | | // enter deep power down mode |
| | | // board_led_on(BOARD_LED_2); |
| | | power_enter_power_down_mode(1); |
| | | // board_led_off(BOARD_LED_2); |
| | | wakeup_from_power_down = 1; |
| | | } |
| | | else |
| | | { |
| | | power_enter_shelf_mode(); |
| | | } |
| | | |
| | | power_clear_if_power_mode(); |
| | | #ifdef UWB_EN |
| | | power_mode_clear(POWER_UNIT_UWB); |
| | | #endif |
| | | } |
| | | |
| | | // uint32_t int_pending = REG_READ(0xE000E200); |
| | | int_unlock(lock); |
| | | |
| | | if (wakeup_from_power_down) |
| | | { |
| | | #ifdef UWB_EN |
| | | LOG_INFO(TRACE_MODULE_DRIVER, "Wakeup from power down %u\r\n", slp_cnt); |
| | | #else |
| | | LOG_INFO(TRACE_MODULE_DRIVER, "Wakeup from power down\r\n"); |
| | | #endif |
| | | // TODO: restore app configuration if needed |
| | | app_restore_from_power_down(); |
| | | } |
| | | } |
| | | |
| | | __WEAK void board_prepare_for_power_down(void) |
| | | { |
| | | } |
| | | |
| | | __WEAK void board_restore_from_power_down(void) |
| | | { |
| | | } |
| | | |
| | | __WEAK void app_restore_from_power_down(void) |
| | | { |
| | | } |