//**************************************************************************** // //! \brief Setting various wake sources for the device //! //! \param target is the lowest power mode that the deveice will exercise //! //! \return 0 if success, -1 in case of error // //**************************************************************************** int set_wkup_srcs(enum soc_pm target) { int iRetVal = -1; switch(target) { case e_pm_S0: case e_pm_S1: case e_pm_S2: /* These handle the cases of run, sleep, deepsleep. Wake source is configured outside this scope in individual peripherals */ break; case e_pm_S3: if(lpds_wk_info.wk_type & WK_RTC) { /* Setup the LPDS wake time */ MAP_PRCMLPDSIntervalSet(lpds_wk_info.timer_interval * 32768); /* Enable the wake source to be timer */ MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_TIMER); iRetVal = 0; } if(lpds_wk_info.wk_type & WK_GPIO) { MAP_PRCMLPDSWakeUpGPIOSelect(lpds_wk_info.wk_gpio_pin,lpds_wk_info.trigger_type); MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_GPIO); iRetVal = 0; } if(lpds_wk_info.wk_type & WK_HOST_IRQ) { /* Set LPDS Wakeup source as NWP request */ MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_HOST_IRQ); iRetVal = 0; } if(lpds_wk_info.is_periodic == false) { lpds_wk_info.wk_type &= (~WK_RTC); } break; case e_pm_S4: if(hib_wk_info.wk_type & WK_RTC) { /* Setup the LPDS wake time */ MAP_PRCMHibernateIntervalSet(hib_wk_info.timer_interval * 32768); /* Enable the wake source to be timer */ MAP_PRCMHibernateWakeupSourceEnable(PRCM_HIB_SLOW_CLK_CTR); iRetVal = 0; } if(hib_wk_info.wk_type & WK_GPIO) { MAP_PRCMHibernateWakeUpGPIOSelect(hib_wk_info.wk_gpio_pin,hib_wk_info.trigger_type); MAP_PRCMHibernateWakeupSourceEnable(hib_wk_info.wk_gpio_pin); iRetVal = 0; } break; default: return -1; } return iRetVal; }
STATIC void pin_irq_enable (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) { // enable GPIO as a wake source during LPDS MAP_PRCMLPDSWakeUpGPIOSelect(idx, pybpin_wake_pin[idx].lpds); MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_GPIO); } if (pybpin_wake_pin[idx].hib != PYBPIN_WAKES_NOT) { // enable GPIO as a wake source during hibernate MAP_PRCMHibernateWakeUpGPIOSelect(hib_pin, pybpin_wake_pin[idx].hib); MAP_PRCMHibernateWakeupSourceEnable(hib_pin); } else { MAP_PRCMHibernateWakeupSourceDisable(hib_pin); } } // if idx is invalid, the pin supports active interrupts for sure if (idx >= PYBPIN_NUM_WAKE_PINS || pybpin_wake_pin[idx].active) { MAP_GPIOIntClear(self->port, self->bit); MAP_GPIOIntEnable(self->port, self->bit); } // in case it was enabled before else if (idx < PYBPIN_NUM_WAKE_PINS && !pybpin_wake_pin[idx].active) { MAP_GPIOIntDisable(self->port, self->bit); } }
/* GPIO based wakeup from S3(LPDS) */ static i32 check_n_setup_S3_wakeup_from_gpio() { i32 retval, indx; u8 gpio_num[MAX_GPIO_WAKESOURCE]; u8 int_type[MAX_GPIO_WAKESOURCE]; /* Check for any special purpose GPIO usage */ retval = cc_gpio_get_spl_purpose(&gpio_num[0], &int_type[0], MAX_GPIO_WAKESOURCE); if(retval > 0) { for(indx = 0; indx < sizeof(gpio_wake_src); indx++) { if(gpio_wake_src[indx] == gpio_num[0]) { /* Setup the GPIO to be the wake source */ MAP_PRCMLPDSWakeUpGPIOSelect( indx, gpio_lpds_inttype[int_type[0]]); MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_GPIO); /* Save the GPIO number wake from LPDS */ cc_pm_ctrl.spl_gpio_wakefrom_lpds = gpio_num[0]; break; } } } else { return -1; } return 0; }
STATIC bool setup_timer_lpds_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_LPDS) { // subtract the time it takes to wakeup from lpds t_remaining -= WAKEUP_TIME_LPDS; t_remaining = (t_remaining > 0xFFFFFFFF) ? 0xFFFFFFFF: t_remaining; // setup the LPDS wake time MAP_PRCMLPDSIntervalSet((uint32_t)t_remaining); // enable the wake source MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_TIMER); return true; } // disable the timer as wake source MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER); 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; }
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); }
/* Network (Host IRQ) based wakeup from S3(LPDS) */ static i32 setup_S3_wakeup_from_nw() { #define IS_NWPIC_INTR_SET() (HWREG(NVIC_EN5) & (1 << ((INT_NWPIC - 16) & 31))) /* Check if the NWP->APPs interrupt is enabled */ if(IS_NWPIC_INTR_SET()) { /* Set LPDS Wakeup source as NWP request */ MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_HOST_IRQ); return 0; } else { return -1; } }
/* Sets up wake-up sources for indicated power mode */ i32 cc_set_up_wkup_srcs(enum soc_pm target) { i32 nw_ret = -1, gpio_ret = -1, timer_ret = -1; switch(target) { case e_pm_S0: case e_pm_S1: case e_pm_S2: /* These handle the cases of run, sleep, deepsleep. Wake source is configured outside this scope in individual peripherals */ break; case e_pm_S3: /* Low power deep sleep condition */ /* Network (Host IRQ) based wakeup is always enabled */ nw_ret = setup_S3_wakeup_from_nw(); /* Check and enable GPIO based wakeup */ gpio_ret = check_n_setup_S3_wakeup_from_gpio(); /* Check and enable LRT based wakeup */ timer_ret = check_n_setup_S3_wakeup_from_timer(); break; case e_pm_S4: /* Hibernate condition */ /* Check and enable GPIO based wakeup */ gpio_ret = check_n_setup_S4_wakeup_from_gpio(); /* Check and enable LRT based wakeup */ timer_ret = check_n_setup_S4_wakeup_from_timer(); break; default: break; } if(ERR_TIMER_TO_WAKE == timer_ret) { return -1; } if((nw_ret < 0) && (gpio_ret < 0) && (timer_ret < 0)) { return -1; } else if((gpio_ret < 0) && (timer_ret < 0)) { /* Setup the LPDS wake time */ MAP_PRCMLPDSIntervalSet(LPDS_WDOG_TIME); /* Enable the wake source to be timer */ MAP_PRCMLPDSWakeupSourceEnable( PRCM_LPDS_TIMER); } return 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; }
/* * ======== PowerCC3200_sleepPolicy ======== */ void PowerCC3200_sleepPolicy() { bool returnFromSleep = FALSE; uint32_t constraintMask; uint32_t ticks; uint64_t time; uint64_t match; uint64_t curr; uint64_t remain; uint32_t taskKey; uint32_t swiKey; /* disable interrupts */ CPUcpsid(); /* disable Swi and Task scheduling */ swiKey = Swi_disable(); taskKey = Task_disable(); /* query the declared constraints */ constraintMask = Power_getConstraintMask(); /* * Do not go into LPDS if not allowed into DEEPSLEEP. * Check to see if we can go into LPDS (lowest level sleep). * If not allowed, then attempt to go into DEEPSLEEP. * If not allowed in DEEPSLEEP then just SLEEP. */ /* check if we are allowed to go to LPDS */ if ((constraintMask & ((1 << PowerCC3200_DISALLOW_LPDS) | (1 << PowerCC3200_DISALLOW_DEEPSLEEP))) == 0) { /* * Check how many ticks until the next scheduled wakeup. A value of * zero indicates a wakeup will occur as the current Clock tick period * expires; a very large value indicates a very large number of Clock * tick periods will occur before the next scheduled wakeup. */ /* Get the time remaining for the RTC timer to expire */ ticks = Clock_getTicksUntilInterrupt(); /* convert ticks to microseconds */ time = ticks * Clock_tickPeriod; /* check if can go to LPDS */ if (time > Power_getTransitionLatency(PowerCC3200_LPDS, Power_TOTAL)) { /* get the current and match values for RTC */ match = MAP_PRCMSlowClkCtrMatchGet(); curr = MAP_PRCMSlowClkCtrGet(); remain = match - curr - (((uint64_t)PowerCC3200_TOTALTIMELPDS * 32768) / 1000000); /* set the LPDS wakeup time interval */ MAP_PRCMLPDSIntervalSet(remain); /* enable the wake source to be timer */ MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_TIMER); /* go to LPDS mode */ Power_sleep(PowerCC3200_LPDS); /* set 'returnFromSleep' to TRUE*/ returnFromSleep = TRUE; } } /* check if we are allowed to go to DEEPSLEEP */ if ((constraintMask & (1 << PowerCC3200_DISALLOW_DEEPSLEEP) == 0) && (!returnFromSleep)) { /* * Check how many ticks until the next scheduled wakeup. A value of * zero indicates a wakeup will occur as the current Clock tick period * expires; a very large value indicates a very large number of Clock * tick periods will occur before the next scheduled wakeup. */ ticks = Clock_getTicksUntilInterrupt(); /* convert ticks to microseconds */ time = ticks * Clock_tickPeriod; /* check if can go to DEEPSLEEP */ if (time > Power_getTransitionLatency(PowerCC3200_DEEPSLEEP, Power_TOTAL)) { /* schedule the wakeup event */ ticks -= PowerCC3200_RESUMETIMEDEEPSLEEP / Clock_tickPeriod; Clock_setTimeout(Clock_handle(&clockObj), ticks); Clock_start(Clock_handle(&clockObj)); /* go to DEEPSLEEP mode */ Power_sleep(PowerCC3200_DEEPSLEEP); Clock_stop(Clock_handle(&clockObj)); /* set 'returnFromSleep' to TRUE so we don't go to sleep (below) */ returnFromSleep = TRUE; } } /* re-enable interrupts */ CPUcpsie(); /* restore Swi scheduling */ Swi_restore(swiKey); /* restore Task scheduling */ Task_restore(taskKey); /* sleep only if we are not returning from one of the sleep modes above */ if (!(returnFromSleep)) { MAP_PRCMSleepEnter(); } }