These are useful for dealing with low power situations, though powman's time keeping leaves a little to be desired.
150 lines
5.3 KiB
C
150 lines
5.3 KiB
C
|
|
#include "powman.h"
|
|
|
|
static powman_power_state off_state;
|
|
static powman_power_state on_state;
|
|
|
|
//#define DEBUG
|
|
|
|
uint8_t powman_get_wake_reason(void) {
|
|
// 0 = chip reset, for the source of the last reset see POWMAN_CHIP_RESET
|
|
// 1 = pwrup0 (GPIO interrupt 0)
|
|
// 2 = pwrup1 (GPIO interrupt 1)
|
|
// 3 = pwrup2 (GPIO interrupt 2)
|
|
// 4 = pwrup3 (GPIO interrupt 3)
|
|
// 5 = coresight_pwrup
|
|
// 6 = alarm_pwrup (timeout or alarm wakeup)
|
|
return powman_hw->last_swcore_pwrup & 0x7f;
|
|
}
|
|
|
|
void powman_init() {
|
|
uint64_t abs_time_ms = 1746057600000; // 2025/05/01 - Milliseconds since epoch
|
|
|
|
// Run everything from pll_usb pll and stop pll_sys
|
|
set_sys_clock_48mhz();
|
|
|
|
// Use the 32768 Hz clockout from the RTC to keep time accurately
|
|
//clock_configure_gpin(clk_ref, 12, 32.768f * KHZ, 32.768f * KHZ);
|
|
//clock_configure_gpin(clk_sys, 12, 32.768f * KHZ, 32.768f * KHZ);
|
|
//clock_configure_undivided(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 32.768f * KHZ);
|
|
//powman_timer_set_1khz_tick_source_lposc_with_hz(32768);
|
|
|
|
powman_timer_set_1khz_tick_source_lposc();
|
|
pll_deinit(pll_sys);
|
|
|
|
// Set all pins to input (as far as SIO is concerned)
|
|
gpio_set_dir_all_bits(0);
|
|
for (int i = 0; i < NUM_BANK0_GPIOS; ++i) {
|
|
gpio_set_function(i, GPIO_FUNC_SIO);
|
|
if (i > NUM_BANK0_GPIOS - NUM_ADC_CHANNELS) {
|
|
gpio_disable_pulls(i);
|
|
gpio_set_input_enabled(i, false);
|
|
}
|
|
}
|
|
|
|
// Unlock the VREG control interface
|
|
hw_set_bits(&powman_hw->vreg_ctrl, POWMAN_PASSWORD_BITS | POWMAN_VREG_CTRL_UNLOCK_BITS);
|
|
|
|
// Turn off USB PHY and apply pull downs on DP & DM
|
|
usb_hw->phy_direct = USB_USBPHY_DIRECT_TX_PD_BITS | USB_USBPHY_DIRECT_RX_PD_BITS | USB_USBPHY_DIRECT_DM_PULLDN_EN_BITS | USB_USBPHY_DIRECT_DP_PULLDN_EN_BITS;
|
|
|
|
usb_hw->phy_direct_override = USB_USBPHY_DIRECT_RX_DM_BITS | USB_USBPHY_DIRECT_RX_DP_BITS | USB_USBPHY_DIRECT_RX_DD_BITS |
|
|
USB_USBPHY_DIRECT_OVERRIDE_TX_DIFFMODE_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DM_PULLUP_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_FSSLEW_OVERRIDE_EN_BITS |
|
|
USB_USBPHY_DIRECT_OVERRIDE_TX_PD_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_RX_PD_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DM_OVERRIDE_EN_BITS |
|
|
USB_USBPHY_DIRECT_OVERRIDE_TX_DP_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DM_OE_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DP_OE_OVERRIDE_EN_BITS |
|
|
USB_USBPHY_DIRECT_OVERRIDE_DM_PULLDN_EN_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLDN_EN_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS |
|
|
USB_USBPHY_DIRECT_OVERRIDE_DM_PULLUP_HISEL_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_HISEL_OVERRIDE_EN_BITS;
|
|
|
|
// start powman and set the time
|
|
powman_timer_start();
|
|
powman_timer_set_ms(abs_time_ms);
|
|
|
|
// Allow power down when debugger connected
|
|
powman_set_debug_power_request_ignored(true);
|
|
|
|
// Power states
|
|
powman_power_state P1_7 = POWMAN_POWER_STATE_NONE;
|
|
|
|
powman_power_state P0_3 = POWMAN_POWER_STATE_NONE;
|
|
P0_3 = powman_power_state_with_domain_on(P0_3, POWMAN_POWER_DOMAIN_SWITCHED_CORE);
|
|
P0_3 = powman_power_state_with_domain_on(P0_3, POWMAN_POWER_DOMAIN_XIP_CACHE);
|
|
|
|
off_state = P1_7;
|
|
on_state = P0_3;
|
|
}
|
|
|
|
// Initiate power off
|
|
int __no_inline_not_in_flash_func(powman_off)(void) {
|
|
// Set power states
|
|
bool valid_state = powman_configure_wakeup_state(off_state, on_state);
|
|
if (!valid_state) {
|
|
return PICO_ERROR_INVALID_STATE;
|
|
}
|
|
|
|
// reboot to main
|
|
powman_hw->boot[0] = 0;
|
|
powman_hw->boot[1] = 0;
|
|
powman_hw->boot[2] = 0;
|
|
powman_hw->boot[3] = 0;
|
|
|
|
// Switch to required power state
|
|
int rc = powman_set_power_state(off_state);
|
|
if (rc != PICO_OK) {
|
|
return rc;
|
|
}
|
|
|
|
// Power down
|
|
while (true) __wfi();
|
|
}
|
|
|
|
int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms) {
|
|
gpio_init(gpio);
|
|
gpio_set_dir(gpio, false);
|
|
gpio_set_input_enabled(gpio, true);
|
|
|
|
// Must set pulls here, or our pin may never go into its idle state
|
|
gpio_set_pulls(gpio, !high, high);
|
|
|
|
// If the pin is currently in a triggered state, wait for idle
|
|
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
|
if (gpio_get(gpio) == high) {
|
|
while(gpio_get(gpio) == high) {
|
|
sleep_ms(10);
|
|
if(time_reached(timeout)) return -1;
|
|
}
|
|
}
|
|
powman_enable_gpio_wakeup(hw_wakeup, gpio, edge, high);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Power off until a gpio goes high
|
|
int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms) {
|
|
powman_init();
|
|
|
|
powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, gpio, edge, high, 1000);
|
|
|
|
if (timeout_ms > 0) {
|
|
uint64_t ms = powman_timer_get_ms();
|
|
return powman_off_until_time(ms + timeout_ms);
|
|
} else {
|
|
return powman_off();
|
|
}
|
|
}
|
|
|
|
// Power off until an absolute time
|
|
int powman_off_until_time(uint64_t absolute_time_ms) {
|
|
powman_init();
|
|
|
|
// Start powman timer and turn off
|
|
powman_enable_alarm_wakeup_at_ms(absolute_time_ms);
|
|
return powman_off();
|
|
}
|
|
|
|
// Power off for a number of milliseconds
|
|
int powman_off_for_ms(uint64_t duration_ms) {
|
|
powman_init();
|
|
|
|
uint64_t ms = powman_timer_get_ms();
|
|
return powman_off_until_time(ms + duration_ms);
|
|
} |