/* * Copyright (c) 2013, Texas Instruments Incorporated - http://www.ti.com/ * 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 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 the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER 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. */ /** * \addtogroup cc2538 * @{ * * \defgroup cc2538-lpm cc2538 Low Power Modes * * Driver for the cc2538 power modes * @{ * * \file * Header file with register, macro and function declarations for the cc2538 * low power module */ #ifndef LPM_H_ #define LPM_H_ #include "contiki-conf.h" #include "rtimer.h" #include #include /*---------------------------------------------------------------------------*/ /** * \name LPM stats * * Maintains a record of how many rtimer ticks spent in each Power Mode. * Mainly used for debugging the module * @{ */ #if LPM_CONF_STATS extern rtimer_clock_t lpm_stats[3]; /** * \brief Read the time spent in a PM in rtimer ticks * \param pm The pm as a value in [0,2] */ #define LPM_STATS_GET(pm) lpm_stats[pm] #else #define LPM_STATS_GET(pm) #endif /** @} */ /*---------------------------------------------------------------------------*/ /** * \name Constants to be used as arguments to lpm_set_max_pm() * @{ */ #define LPM_PM0 0 #define LPM_PM1 1 #define LPM_PM2 2 /** @} */ /*---------------------------------------------------------------------------*/ typedef bool (*lpm_periph_permit_pm1_func_t)(void); #if LPM_CONF_ENABLE /** * \brief Initialise the LPM module */ void lpm_init(void); /** * \brief Drop to Deep Sleep * * This function triggers a sequence to enter Deep Sleep. The sequence involves * determining the most suitable PM and switching the system clock source to * the 16MHz if required. If the energest module is enabled, the sequence also * performs some simple energest calculations. * * Broadly speaking, this function will be called from the main loop when all * events have been serviced. This functions aims to be clever enough in order * to be able to choose between PMs 0/1/2 depending on chip status and * anticipated sleep duration. This choice is made subject to configuration * restrictions and subject to restrictions imposed by calls to * lpm_set_max_pm(). * * This PM selection heuristic has the following primary criteria: * - Is the RF off? * - Are all registered peripherals permitting PM1+? * - Is the Sleep Timer scheduled to fire an interrupt? * * If the answer to any of those questions is no, we will drop to PM0 and * will wake up to any interrupt. Best case scenario (if nothing else happens), * we will idle until the next SysTick in no more than 1000/CLOCK_SECOND ms * (7.8125ms). * * If all can be answered with 'yes', we can drop to PM1/2 knowing that the * Sleep Timer will wake us up. Depending on the estimated deep sleep duration * and the max PM allowed by user configuration, we select the most efficient * Power Mode to drop to. If the duration is too short, we simply IDLE in PM0. * * Dropping to PM1/2 requires a switch to the 16MHz OSC. We have the option of * letting the SoC do this for us automatically. However, if an interrupt fires * during this automatic switch, we will need to re-assert WFI. To avoid this * complexity, we perform the switch to the 16MHz OSC manually in software and * we assert WFI after the transition has been completed. This gives us a * chance to bail out if an interrupt fires or an event is raised during the * transition. If nothing happens, dropping to PM1+ is un-interruptible and * with a deterministic duration. When we wake up, we switch back to the 32MHz * OSC manually before handing control back to main. This is implemented in * lpm_exit(), which will always be called from within the Sleep Timer ISR * context. * * \note Dropping to PM2 means that data in the SRAM non-retention area will * be lost. It is recommended to disable PM2 if the total RAM footprint is * larger than what will fit in the retention area. * .nrdata* sections can be used to place uninitialized data in the SRAM * non-retention area. * * \sa main(), rtimer_arch_next_trigger(), lpm_exit(), lpm_set_max_pm() */ void lpm_enter(void); /** * \brief Perform an 'Exit Deep Sleep' sequence * * This routine is called from within the context of the ISR that caused us to * come out of PM1/2. It performs a wake up sequence to make sure the 32MHz OSC * is back on and the system clock is sourced on it. * * While in PMs 1 and 2, the system clock stops ticking. This functions adjusts * it when we wake up. * * We always exit PM1/2 as a result of a scheduled rtimer task or a GPIO * interrupt. This may lead to other parts of the code trying to use the RF, * so we need to switch the clock source \e before said code gets executed. * * This function also makes sure that the sleep timer value is up-to-date * following wake-up from PM1/2 so that RTIMER_NOW() works. * * \note This function should be called at the very beginning of ISRs waking up * the SoC in order to restore all clocks and timers. * * \sa lpm_enter(), rtimer_isr() */ void lpm_exit(void); /** * \brief Prevent the SoC from dropping to a PM higher than \e max_pm * \param pm The highest PM we are allowed to enter, specified as a * number in [0, 2] * * Defines for the \e pm argument are LPM_PMx. * * This function can be used by software in situations where some power * modes are undesirable. If, for example, an application needs to avoid PM2, * it would call lpm_set_max_pm(LPM_PM1). * If an application wants to avoid PM1 as well, it would call * lpm_set_max_pm(LPM_PM0) * * PM0 can not be disabled at runtime. Use LPM_CONF_ENABLE to disable LPM * support altogether * * \note If the value of argument \e pm is greater than the value of the * LPM_CONF_MAX_PM configuration directive, LPM_CONF_MAX_PM is used. Thus * if LPM_CONF_MAX_PM==1, calling lpm_set_max_pm(LPM_PM2) would * result in a maximum PM set to 1 and all subsequent Deep Sleeps would * be limited to either PM0 or PM1. * * \sa lpm_enter() */ void lpm_set_max_pm(uint8_t pm); /** * \brief Register a peripheral function which will get called by the LPM * module to get 'permission' to drop to PM1+ * \param permit_pm1_func Pointer to the function * * Some peripherals are sensitive to PM changes. For instance, we don't want to * drop to PM1+ if the USB PLL is active or if the UART TX FIFO is not clear. * * When changing power modes, the LPM driver will call all FPs registered with * this function. The peripheral's function will return true or false to permit * / prohibit PM1+ respectively. If at least one peripheral returns false, the * SoC will drop to PM0 Deep Sleep instead. * * Registering several times the same function makes the LPM module behave as if * the function had been registered once. */ void lpm_register_peripheral(lpm_periph_permit_pm1_func_t permit_pm1_func); /*---------------------------------------------------------------------------*/ /* Disable the entire module if required */ #else #define lpm_init() #define lpm_enter() #define lpm_exit() static inline void lpm_set_max_pm(uint8_t pm) { } static inline void lpm_register_peripheral(lpm_periph_permit_pm1_func_t permit_pm1_func) { } #endif #endif /* LPM_H_ */ /** * @} * @} */