void pyb_sleep_sleep (void) { nlr_buf_t nlr; // check if we should enable timer wake-up if (pybsleep_data.rtc_obj->irq_enabled && (pybsleep_data.rtc_obj->pwrmode & PYB_PWR_MODE_LPDS)) { if (!setup_timer_lpds_wake()) { // lpds entering is not possible, wait for the forced interrupt and return mp_hal_delay_ms(FAILED_SLEEP_DELAY_MS); return; } } else { // disable the timer as wake source MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER); } // do we need network wake-up? if (pybsleep_data.wlan_obj->irq_enabled) { MAP_PRCMLPDSWakeupSourceEnable (PRCM_LPDS_HOST_IRQ); server_sleep_sockets(); } else { MAP_PRCMLPDSWakeupSourceDisable (PRCM_LPDS_HOST_IRQ); } // entering and exiting suspended mode must be an atomic operation // therefore interrupts need to be disabled uint primsk = disable_irq(); if (nlr_push(&nlr) == 0) { pyb_sleep_suspend_enter(); nlr_pop(); } // an exception is always raised when exiting suspend mode enable_irq(primsk); }
void pyb_sleep_init0 (void) { // initialize the sleep objects list mp_obj_list_init(&MP_STATE_PORT(pyb_sleep_obj_list), 0); // register and enable the PRCM interrupt osi_InterruptRegister(INT_PRCM, (P_OSI_INTR_ENTRY)PRCMInterruptHandler, INT_PRIORITY_LVL_1); // disable all LPDS and hibernate wake up sources (WLAN is disabed/enabled before entering LDPS mode) MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_GPIO); MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER); MAP_PRCMHibernateWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR | PRCM_HIB_GPIO2 | PRCM_HIB_GPIO4 | PRCM_HIB_GPIO13 | PRCM_HIB_GPIO17 | PRCM_HIB_GPIO11 | PRCM_HIB_GPIO24 | PRCM_HIB_GPIO26); // check the reset casue (if it's soft reset, leave it as it is) if (pybsleep_reset_cause != PYB_SLP_SOFT_RESET) { switch (MAP_PRCMSysResetCauseGet()) { case PRCM_POWER_ON: pybsleep_reset_cause = PYB_SLP_PWRON_RESET; break; case PRCM_CORE_RESET: case PRCM_MCU_RESET: case PRCM_SOC_RESET: pybsleep_reset_cause = PYB_SLP_HARD_RESET; break; case PRCM_WDT_RESET: pybsleep_reset_cause = PYB_SLP_WDT_RESET; break; case PRCM_HIB_EXIT: if (PRCMGetSpecialBit(PRCM_WDT_RESET_BIT)) { pybsleep_reset_cause = PYB_SLP_WDT_RESET; } else { pybsleep_reset_cause = PYB_SLP_HIB_RESET; // set the correct wake reason switch (MAP_PRCMHibernateWakeupCauseGet()) { case PRCM_HIB_WAKEUP_CAUSE_SLOW_CLOCK: pybsleep_wake_reason = PYB_SLP_WAKED_BY_RTC; // TODO repeat the alarm break; case PRCM_HIB_WAKEUP_CAUSE_GPIO: pybsleep_wake_reason = PYB_SLP_WAKED_BY_GPIO; break; default: break; } } break; default: break; } } }
STATIC bool setup_timer_hibernate_wake (void) { uint64_t t_match, t_curr; int64_t t_remaining; // get the time remaining for the RTC timer to expire t_match = MAP_PRCMSlowClkCtrMatchGet(); t_curr = MAP_PRCMSlowClkCtrGet(); // get the time remaining in terms of slow clocks t_remaining = (t_match - t_curr); if (t_remaining > WAKEUP_TIME_HIB) { // subtract the time it takes for wakeup from hibernate t_remaining -= WAKEUP_TIME_HIB; // setup the LPDS wake time MAP_PRCMHibernateIntervalSet((uint32_t)t_remaining); // enable the wake source MAP_PRCMHibernateWakeupSourceEnable(PRCM_HIB_SLOW_CLK_CTR); return true; } // disable the timer as wake source MAP_PRCMLPDSWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR); uint32_t f_seconds; uint16_t f_mseconds; // setup a timer interrupt immediately pyb_rtc_calc_future_time (FORCED_TIMER_INTERRUPT_MS, &f_seconds, &f_mseconds); MAP_PRCMRTCMatchSet(f_seconds, f_mseconds); // LPDS wake by timer was not possible, force an interrupt in active mode instead MAP_PRCMIntEnable(PRCM_INT_SLOW_CLK_CTR); return false; }
STATIC void pin_irq_disable (mp_obj_t self_in) { const pin_obj_t *self = self_in; uint hib_pin, idx; pin_get_hibernate_pin_and_idx (self, &hib_pin, &idx); if (idx < PYBPIN_NUM_WAKE_PINS) { if (pybpin_wake_pin[idx].lpds != PYBPIN_WAKES_NOT) { // disable GPIO as a wake source during LPDS MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_GPIO); } if (pybpin_wake_pin[idx].hib != PYBPIN_WAKES_NOT) { // disable GPIO as a wake source during hibernate MAP_PRCMHibernateWakeupSourceDisable(hib_pin); } } // not need to check for the active flag, it's safe to disable it anyway MAP_GPIOIntDisable(self->port, self->bit); }
void pyb_sleep_deepsleep (void) { // check if we should enable timer wake-up if (pybsleep_data.rtc_obj->irq_enabled && (pybsleep_data.rtc_obj->pwrmode & PYB_PWR_MODE_HIBERNATE)) { if (!setup_timer_hibernate_wake()) { // hibernating is not possible, wait for the forced interrupt and return mp_hal_delay_ms(FAILED_SLEEP_DELAY_MS); return; } } else { // disable the timer as hibernate wake source MAP_PRCMLPDSWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR); } wlan_stop(SL_STOP_TIMEOUT); pyb_sleep_flash_powerdown(); // must be done just before entering hibernate mode pyb_sleep_iopark(true); MAP_PRCMHibernateEnter(); }
/* Timer based wakeup from S3 (LPDS) */ static i32 check_n_setup_S3_wakeup_from_timer() { u64 scc_match, scc_curr, scc_remaining; /* Check if there is an alarm set */ if(cc_rtc_has_alarm()) { /* Get the time remaining for the RTC timer to expire */ scc_match = MAP_PRCMSlowClkCtrMatchGet(); scc_curr = MAP_PRCMSlowClkCtrGet(); if(scc_match > scc_curr) { /* Get the time remaining in terms of slow clocks */ scc_remaining = (scc_match - scc_curr); if(scc_remaining > WAKEUP_TIME_LPDS) { /* Subtract the time it takes for wakeup from S3 (LPDS) */ scc_remaining -= WAKEUP_TIME_LPDS; scc_remaining = (scc_remaining > 0xFFFFFFFF)? 0xFFFFFFFF: scc_remaining; /* Setup the LPDS wake time */ MAP_PRCMLPDSIntervalSet( (u32)scc_remaining); /* Enable the wake source to be timer */ MAP_PRCMLPDSWakeupSourceEnable( PRCM_LPDS_TIMER); } else { /* Cannot enter LPDS */ return ERR_TIMER_TO_WAKE; } } else { return ERR_TIMER_TO_WAKE; } } else { /* Disable timer as the wake source */ MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER); return -1; } return 0; }
STATIC void PRCMInterruptHandler (void) { // reading the interrupt status automatically clears the interrupt if (PRCM_INT_SLOW_CLK_CTR == MAP_PRCMIntStatus()) { // reconfigure it again (if repeat is true) pyb_rtc_repeat_alarm (pybsleep_data.rtc_obj); pybsleep_data.rtc_obj->irq_flags = PYB_RTC_ALARM0; // need to check if irq's are enabled from the user point of view if (pybsleep_data.rtc_obj->irq_enabled && (pybsleep_data.rtc_obj->pwrmode & PYB_PWR_MODE_ACTIVE)) { mp_irq_handler(pybsleep_data.rtc_obj->irq_obj); } pybsleep_data.rtc_obj->irq_flags = 0; } else { // interrupt has been triggered while waking up from LPDS switch (MAP_PRCMLPDSWakeupCauseGet()) { case PRCM_LPDS_HOST_IRQ: pybsleep_data.wlan_obj->irq_flags = MODWLAN_WIFI_EVENT_ANY; mp_irq_handler(pybsleep_data.wlan_obj->irq_obj); pybsleep_wake_reason = PYB_SLP_WAKED_BY_WLAN; pybsleep_data.wlan_obj->irq_flags = 0; break; case PRCM_LPDS_GPIO: mp_irq_handler(pybsleep_data.gpio_lpds_wake_cb); pybsleep_wake_reason = PYB_SLP_WAKED_BY_GPIO; break; case PRCM_LPDS_TIMER: // reconfigure it again if repeat is true pyb_rtc_repeat_alarm (pybsleep_data.rtc_obj); pybsleep_data.rtc_obj->irq_flags = PYB_RTC_ALARM0; // next one clears the wake cause flag MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER); mp_irq_handler(pybsleep_data.rtc_obj->irq_obj); pybsleep_data.rtc_obj->irq_flags = 0; pybsleep_wake_reason = PYB_SLP_WAKED_BY_RTC; break; default: break; } } }
/// \method irq(trigger, priority, handler, wake) STATIC mp_obj_t pin_irq (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { mp_arg_val_t args[mp_irq_INIT_NUM_ARGS]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, mp_irq_INIT_NUM_ARGS, mp_irq_init_args, args); pin_obj_t *self = pos_args[0]; // convert the priority to the correct value uint priority = mp_irq_translate_priority (args[1].u_int); // verify and translate the interrupt mode uint mp_trigger = mp_obj_get_int(args[0].u_obj); uint trigger; if (mp_trigger == (PYB_PIN_FALLING_EDGE | PYB_PIN_RISING_EDGE)) { trigger = GPIO_BOTH_EDGES; } else { switch (mp_trigger) { case PYB_PIN_FALLING_EDGE: trigger = GPIO_FALLING_EDGE; break; case PYB_PIN_RISING_EDGE: trigger = GPIO_RISING_EDGE; break; case PYB_PIN_LOW_LEVEL: trigger = GPIO_LOW_LEVEL; break; case PYB_PIN_HIGH_LEVEL: trigger = GPIO_HIGH_LEVEL; break; default: goto invalid_args; } } uint8_t pwrmode = (args[3].u_obj == mp_const_none) ? PYB_PWR_MODE_ACTIVE : mp_obj_get_int(args[3].u_obj); if (pwrmode > (PYB_PWR_MODE_ACTIVE | PYB_PWR_MODE_LPDS | PYB_PWR_MODE_HIBERNATE)) { goto invalid_args; } // get the wake info from this pin uint hib_pin, idx; pin_get_hibernate_pin_and_idx ((const pin_obj_t *)self, &hib_pin, &idx); if (pwrmode & PYB_PWR_MODE_LPDS) { if (idx >= PYBPIN_NUM_WAKE_PINS) { goto invalid_args; } // wake modes are different in LDPS uint wake_mode; switch (trigger) { case GPIO_FALLING_EDGE: wake_mode = PRCM_LPDS_FALL_EDGE; break; case GPIO_RISING_EDGE: wake_mode = PRCM_LPDS_RISE_EDGE; break; case GPIO_LOW_LEVEL: wake_mode = PRCM_LPDS_LOW_LEVEL; break; case GPIO_HIGH_LEVEL: wake_mode = PRCM_LPDS_HIGH_LEVEL; break; default: goto invalid_args; break; } // first clear the lpds value from all wake-able pins for (uint i = 0; i < PYBPIN_NUM_WAKE_PINS; i++) { pybpin_wake_pin[i].lpds = PYBPIN_WAKES_NOT; } // enable this pin as a wake-up source during LPDS pybpin_wake_pin[idx].lpds = wake_mode; } else if (idx < PYBPIN_NUM_WAKE_PINS) { // this pin was the previous LPDS wake source, so disable it completely if (pybpin_wake_pin[idx].lpds != PYBPIN_WAKES_NOT) { MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_GPIO); } pybpin_wake_pin[idx].lpds = PYBPIN_WAKES_NOT; } if (pwrmode & PYB_PWR_MODE_HIBERNATE) { if (idx >= PYBPIN_NUM_WAKE_PINS) { goto invalid_args; } // wake modes are different in hibernate uint wake_mode; switch (trigger) { case GPIO_FALLING_EDGE: wake_mode = PRCM_HIB_FALL_EDGE; break; case GPIO_RISING_EDGE: wake_mode = PRCM_HIB_RISE_EDGE; break; case GPIO_LOW_LEVEL: wake_mode = PRCM_HIB_LOW_LEVEL; break; case GPIO_HIGH_LEVEL: wake_mode = PRCM_HIB_HIGH_LEVEL; break; default: goto invalid_args; break; } // enable this pin as wake-up source during hibernate pybpin_wake_pin[idx].hib = wake_mode; } else if (idx < PYBPIN_NUM_WAKE_PINS) { pybpin_wake_pin[idx].hib = PYBPIN_WAKES_NOT; } // we need to update the callback atomically, so we disable the // interrupt before we update anything. pin_irq_disable(self); if (pwrmode & PYB_PWR_MODE_ACTIVE) { // register the interrupt pin_extint_register((pin_obj_t *)self, trigger, priority); if (idx < PYBPIN_NUM_WAKE_PINS) { pybpin_wake_pin[idx].active = true; } } else if (idx < PYBPIN_NUM_WAKE_PINS) { pybpin_wake_pin[idx].active = false; } // all checks have passed, we can create the irq object mp_obj_t _irq = mp_irq_new (self, args[2].u_obj, &pin_irq_methods); if (pwrmode & PYB_PWR_MODE_LPDS) { pyb_sleep_set_gpio_lpds_callback (_irq); } // save the mp_trigge for later self->irq_trigger = mp_trigger; // enable the interrupt just before leaving pin_irq_enable(self); return _irq; invalid_args: nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); }