Exemple #1
0
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);
}
Exemple #2
0
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;
        }
    }
}
Exemple #3
0
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;
}
Exemple #4
0
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);
}
Exemple #5
0
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();
}
Exemple #6
0
/* 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;
}
Exemple #7
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;
        }
    }
}
Exemple #8
0
/// \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));
}