1 /***************************************************************************//**
2 * \file cyhal_syspm.h
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon power
6 * management configuration. This interface abstracts out the
7 * chip specific details. If any chip specific functionality is necessary, or
8 * performance is critical the low level functions can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 *     http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29 
30 /**
31 * \addtogroup group_hal_syspm System Power Management
32 * \ingroup group_hal
33 * \{
34 * Interface for changing power states and restricting when they are allowed.
35 *
36 * Power management is handled both at a system level and at a peripheral driver
37 * level. The system wide API (this) allows the user to interact with the product
38 * as a whole. Additionally, each peripheral keeps track of what its state is and
39 * whether it can safely move to a new state while still maintaining any current
40 * operations. To initialize the system wide power management, \ref cyhal_syspm_init
41 * should be called as part of the initial device startup.
42 *
43 * At the System level, the APIs are intended to allow the application to specify
44 * exactly what is happening. It can request changes to both the MCU Power State
45 * as well as the System Wide Power State. There are three supported MCU Power
46 * States:
47 * * Active - This is the normal operating state of the MCU
48 * * Sleep - In this state the MCU is no longer running. It can be woken up again
49 * from an interrupt. This state is reached by calling \ref cyhal_syspm_sleep.
50 * * Deep Sleep - In this state the MCU is no longer running. It can only be woken
51 * up by select interrupts. This state is reached by calling \ref
52 * cyhal_syspm_deepsleep.
53 * <p>Additionally, there are three supported system states:
54 * * Normal (\ref CYHAL_SYSPM_SYSTEM_NORMAL) - This is a normal operating state
55 * for the device. This is exposed by \ref cyhal_syspm_set_system_state.
56 * * Low (\ref CYHAL_SYSPM_SYSTEM_LOW) - This is a lower power operating state
57 * that may be supported by the device. This state often imposes specific
58 * requirements on clock speeds and voltage levels. See the device specific
59 * documentation for any device specific requirements for this mode, or whether
60 * it is even supported. If the device supports this mode, it is exposed by
61 * \ref cyhal_syspm_set_system_state.
62 * * Hibernate - This is the lowest available power state. In this state most of
63 * the chip is powered down. It has a very limited number of wakeup sources, and
64 * may require the device to reboot in order to come back up. It can be accessed
65 * by calling \ref cyhal_syspm_hibernate.
66 
67 * Any time a power state transition is requested a series of callbacks are invoked.
68 * This allows peripherals, or other parts of the application, to confirm they are
69 * not currently doing something that would not work in the requested power state.
70 * HAL Peripheral drivers automatically register these callbacks when they are
71 * initialized. The application also has the option to register a callback
72 * function(s) to be called on requested state transitions by callling \ref
73 * cyhal_syspm_register_callback. If registered, the application level callbacks
74 * are invoked first. This gives the application a chance stop any peripherals, if
75 * appropriate, to ensure the power transition can actually happen. Alternatively
76 * it can directly reject the transition. Each callback registered can specify
77 * the exact set of states ( \ref cyhal_syspm_callback_state_t ) that it should
78 * be called for. Each callback is invoked multiple times as part of the transition
79 * process as defined by \ref cyhal_syspm_callback_mode_t.
80 *
81 * At any point the code can lock the ability to enter deep sleep by calling \ref
82 * cyhal_syspm_lock_deepsleep. This should be done in critical blocks that need to
83 * continue remain active. When the critical work is complete, and the lock is no
84 * longer needed, it can be released by calling \ref cyhal_syspm_unlock_deepsleep.
85 * The lock is a counter with a max count of USHRT_MAX. It must be locked/unlocked
86 * an equal number of times before the device is actually able to enter deep sleep.
87 *
88 * All peripherals are expected to operate in the default Active/Normal power
89 * state. Some peripherals (primarily analog) can operate in lower power states as
90 * well. These drivers will operate in all power states that the hardware supports.
91 *
92 * When power transitions are requested each type of peripheral has a default
93 * behavior. Peripherals that can continue to operate in the requested power mode
94 * do not interfere. Peripherals that are not currently active allow the transition,
95 * but make sure they restore their state if needed for when the device comes back.
96 * Peripherals that are active and cannot continue to work in the requested power
97 * state will block the transition.
98 *
99 * \note The power management functionality available depends on the availability
100 * of the features in the hardware. For detailed information about exactly what is
101 * supported on each device, refer to the Device Datasheet or Technical Reference
102 * Manual (TRM).
103 
104 * \section section_syspm_features Features
105 * This driver provides control over multiple different types of power management
106 * and when those transitions are allowed:
107 * * Change CPU Power State: APIs to allow changing the current CPU state into
108 * one of the lower power CPU states (SLEEP, DEEPSLEEP)
109 * * Change System Power State: An API allows for changing the system wide power
110 * state between one of states (NORMAL, LOW)
111 * - Hibernate: An API that allows to set the hibernate
112 * wakeup source and set the system state to Hibernate.
113 * * General Purpose Power State Transition Callback: APIs allow for
114 * registering/unregistering a callback function to be notified when various power
115 * state transitions happen. If registered, the application can do anything necessary
116 * at power transitions. It can even prevent the transition if need-be.
117 * * Peripheral Specific Power State Transition Callback: APIs allow for
118 * registering/unregistering a callback function to be called when a peripheral with
119 * a CUSTOM power mode strategy exists and a power mode transition is requested.
120 * This allows the application to customize when it is OK for the peripheral to enter
121 * a low power state.
122 * - Lock DeepSleep: APIs to prevent/allow the CPU from going to deep sleep. This
123 * is a convenience API rather than needing to implement a full Transition Callback
124 * handler.
125 *
126 * \section section_syspm_quickstart Quick Start
127 *
128 * Unlike most HAL drivers this does not require initializing an instance object. The
129 * APIs provided here can be called at anytime. See the snippets below for examples
130 * of how to use this driver.
131 *
132 * \section section_syspm_snippets Code Snippets
133 *
134 * \subsection subsection_syspm_snippet_1 Snippet 1: Simple deep sleep locking
135 * The following snippet shows how to use the deep sleep locking APIs to restrict
136 * when the device can enter deep sleep. In between the lock/unlock calls any
137 * attempt to change power modes will automatically be canceled.
138 * \snippet hal_syspm.c snippet_cyhal_syspm_simple_locking
139 *
140 * \subsection subsection_syspm_snippet_2 Snippet 2: Calling different power state functions
141 * The following snippet shows the different functions that exist to change power states
142 * on the device and how they can each be called.
143 * \snippet hal_syspm.c snippet_cyhal_syspm_power_transitions
144 *
145 * \subsection subsection_syspm_snippet_3 Snippet 3: Using callbacks for application power management
146 * The following snippet shows how to use the callback mechanisms to manage whether
147 * it is safe to enter low power modes.
148 * \snippet hal_syspm.c snippet_cyhal_syspm_app_callback
149 */
150 #pragma once
151 
152 #include <stdint.h>
153 #include <stdbool.h>
154 #include "cy_result.h"
155 #include "cyhal_general_types.h"
156 #include "cyhal_hw_types.h"
157 
158 #if defined(__cplusplus)
159 extern "C" {
160 #endif
161 
162 /** \addtogroup group_hal_results_syspm SYSPM HAL Results
163  *  SYSPM specific return codes
164  *  \ingroup group_hal_results
165  *  \{ *//**
166  */
167 
168 /** Incorrect argument passed into a function. */
169 #define CYHAL_SYSPM_RSLT_BAD_ARGUMENT               \
170     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_SYSPM, 0))
171 /** Driver was unable to be initialized. */
172 #define CYHAL_SYSPM_RSLT_INIT_ERROR                 \
173     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_SYSPM, 1))
174 /** Failed to register callback */
175 #define CYHAL_SYSPM_RSLT_CB_REGISTER_ERROR          \
176     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_SYSPM, 2))
177 /** Power Management transition is pending, data cannot be transferred */
178 #define CYHAL_SYSPM_RSLT_ERR_PM_PENDING             \
179     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_SYSPM, 3))
180 /** Functionality not supported on the current platform */
181 #define CYHAL_SYSPM_RSLT_ERR_NOT_SUPPORTED           \
182     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_SYSPM, 4))
183 /** Deepsleep has been locked */
184 #define CYHAL_SYSPM_RSLT_DEEPSLEEP_LOCKED           \
185     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_SYSPM, 5))
186 
187 /**
188  * \}
189  */
190 
191 /** \cond INTERNAL */
192 /** Sentinel value to indicate end of callback list */
193 #define CYHAL_SYSPM_END_OF_LIST ((cyhal_syspm_callback_data_t*)0x789)
194 /** \endcond */
195 
196 /** Enumeration of the system wide power modes. These modes are device specifc and
197  * may not be supported on all devices. Refer to the device specific documentation
198  * or the Data Sheet to determine what is allowed. Devices that do support these
199  * modes may have requirements for adjusting system settings such as clocks or
200  * voltage levels before transition.
201  */
202 typedef enum
203 {
204     CYHAL_SYSPM_SYSTEM_NORMAL,      /**< Normal Mode. */
205     CYHAL_SYSPM_SYSTEM_LOW,         /**< Low Power Mode. */
206 } cyhal_syspm_system_state_t;
207 
208 /** Enumeration of the system Deep Sleep modes. These modes are device specifc and
209  * may not be supported on all devices. Refer to the device specific documentation
210  * or the Data Sheet to determine what is allowed.
211  */
212 typedef enum
213 {
214     CYHAL_SYSPM_SYSTEM_DEEPSLEEP_NONE,  /**< Not Deep Sleep Mode. */
215     CYHAL_SYSPM_SYSTEM_DEEPSLEEP,       /**< Deep Sleep Mode.     */
216     CYHAL_SYSPM_SYSTEM_DEEPSLEEP_RAM,   /**< Deep Sleep RAM Mode. */
217     CYHAL_SYSPM_SYSTEM_DEEPSLEEP_OFF,   /**< Deep Sleep OFF Mode. */
218 } cyhal_syspm_system_deep_sleep_mode_t;
219 
220 /** Flags enum for the hibernate wakeup sources.
221  * \note Not all wakeup sources are valid on devices. Refer to the datasheet for
222  * device specifics.
223  */
224 typedef enum
225 {
226     CYHAL_SYSPM_HIBERNATE_LPCOMP0_LOW   = 0x01U,    /**< Wake on a low logic level for the LPComp0. */
227     CYHAL_SYSPM_HIBERNATE_LPCOMP0_HIGH  = 0x02U,    /**< Wake on a high logic level for the LPComp0. */
228     CYHAL_SYSPM_HIBERNATE_LPCOMP1_LOW   = 0x04U,    /**< Wake on a low logic level for the LPComp1. */
229     CYHAL_SYSPM_HIBERNATE_LPCOMP1_HIGH  = 0x08U,    /**< Wake on a high logic level for the LPComp1. */
230     CYHAL_SYSPM_HIBERNATE_RTC_ALARM     = 0x10U,    /**< Configure the RTC alarm as wakeup source. */
231     CYHAL_SYSPM_HIBERNATE_WDT           = 0x20U,    /**< Configure the WDT interrupt as wakeup source. */
232     CYHAL_SYSPM_HIBERNATE_PINA_LOW      = 0x40U,    /**< Configure a low logic level for the first wakeup-pin.
233                                                         See device datasheet for specific pin. */
234     CYHAL_SYSPM_HIBERNATE_PINA_HIGH     = 0x80U,    /**< Configure a high logic level for the first wakeup-pin.
235                                                         See device datasheet for specific pin. */
236     CYHAL_SYSPM_HIBERNATE_PINB_LOW      = 0x100U,   /**< Configure a low logic level for the second wakeup-pin.
237                                                         See device datasheet for specific pin. */
238     CYHAL_SYSPM_HIBERNATE_PINB_HIGH     = 0x200U    /**< Configure a high logic level for the second wakeup-pin.
239                                                         See device datasheet for specific pin. */
240 } cyhal_syspm_hibernate_source_t;
241 
242 /** Supply voltages whose levels can be specified and queried via \ref cyhal_syspm_set_supply_voltage and
243   * \ref cyhal_syspm_get_supply_voltage, respectively.
244   *
245   * \note Not all supplies which are present are included here. This enum only contains the voltage supplies
246   * whose values are relevant to the operation of one or more HAL drivers.
247   */
248 typedef enum
249 {
250     CYHAL_VOLTAGE_SUPPLY_VDDA = 0u,                       //!< VDDA - Analog supply voltage
251     CYHAL_VOLTAGE_SUPPLY_MAX  = CYHAL_VOLTAGE_SUPPLY_VDDA //!< Alias for the highest value in this enum
252 } cyhal_syspm_voltage_supply_t;
253 
254 /**
255  * Performs any system wide power management initialization that is needed for future operations.
256  * This can include things like unlocking IOs that might have been frozen when entering a low
257  * power state or registering callback functions that are necessary to allow notifications of
258  * power events. This should be called as part of initializing the device in a Board Support Package
259  * (BSP).
260  *
261  * @return Returns CY_RSLT_SUCCESS if the processor successfully entered the hibernate mode,
262  * otherwise error.
263  */
264 cy_rslt_t cyhal_syspm_init(void);
265 
266  /** Sets the system mode to hibernate.
267  *
268  * This function configures the sources to wake up the device from hibernate mode
269  * and then sets the system to hibernate mode.
270  *
271  * In hibernate mode, all internal supplies are off and no internal state is retained.
272  *
273  * Sources can be wakeup pins, LPComparators, Watchdog (WDT) interrupt, or a Real-Time
274  * clock (RTC) alarm (interrupt). Wakeup from system hibernate always results in a device
275  * reset and normal boot process.
276  *
277  * Wakeup pins:
278  *
279  * A wakeup is supported by pins with programmable polarity. These pins
280  * are typically connected to the GPIO pins or on-chip peripherals under some
281  * conditions. See device datasheet to check if this feature is supported and for specific
282  * pin connections. Setting the wakeup pin to this level will cause a wakeup from
283  * system hibernate mode.
284  *
285  * LPComparators:
286  *
287  * A wakeup is supported by LPComps with programmable polarity.
288  * Setting the LPComp to this level will cause a wakeup from system hibernate
289  * mode.
290  *
291  * Watchdog Timer:
292  *
293  * A wakeup is performed by a WDT interrupt.
294  *
295  * Real-time Clock:
296  *
297  * A wakeup is performed by the RTC alarm.
298  *
299  * For information about hibernate behavior, wakeup sources and their assignment in specific
300  * devices, refer to the appropriate device TRM.
301  *
302  * @param[in] wakeup_source
303  * The source to be configured as a wakeup source from the system hibernate power mode,
304  * see @ref cyhal_syspm_hibernate_source_t. The input parameter values can be ORed.
305  * For example, if you want to enable LPComp0 (active high) and WDT, call this function:
306  * cyhal_syspm_hibernate(CYHAL_SYSPM_HIBERNATE_LPCOMP0_HIGH | CYHAL_SYSPM_HIBERNATE_WDT).
307  *
308  * @warning Do not call this function with different polarity levels for the same
309  * wakeup source. For example, do not call a function like this:
310  * cyhal_syspm_hibernate(CYHAL_SYSPM_HIBERNATE_LPCOMP0_LOW | CYHAL_SYSPM_HIBERNATE_LPCOMP0_HIGH);
311  *
312  * @return Returns CY_RSLT_SUCCESS if the processor successfully entered the hibernate mode,
313  * otherwise error.
314  */
315 cy_rslt_t cyhal_syspm_hibernate(cyhal_syspm_hibernate_source_t wakeup_source);
316 
317 /** Set the system-wide state of the device. This is used to change the power state
318  * within the Active power mode. \ref cyhal_syspm_get_system_state can be used to
319  * get the current state.
320  * \note Not all devices support different states within the Active power mode. If
321  * the device does not support changing state, an error will be returned.
322  *
323  * @param[in] state System wide state.
324  *
325  * @return Returns CY_RSLT_SUCCESS if the processor successfully entered the requested system state,
326  * otherwise error.
327  */
328 cy_rslt_t cyhal_syspm_set_system_state(cyhal_syspm_system_state_t state);
329 
330 /** Gets the system-wide state of the device. States can be changed by calling
331  * \ref cyhal_syspm_set_system_state.
332  * \note Not all devices support different states within the Active power mode. If
333  * the device does not support changing state, this will return \ref
334  * CYHAL_SYSPM_SYSTEM_NORMAL.
335  * @return Returns the current system-wide power state of the device.
336  */
337 cyhal_syspm_system_state_t cyhal_syspm_get_system_state(void);
338 
339 /** Register the specified handler with the power manager to be notified of power
340  * state changes. This is intended for application wide decisions. Peripherals handle
341  * their ability to perform power transitions internally. This callback will be called
342  * before any of the peripheral callbacks for \ref CYHAL_SYSPM_CHECK_READY and
343  * \ref CYHAL_SYSPM_BEFORE_TRANSITION. This callback will be called after all peripheral
344  * callback for \ref CYHAL_SYSPM_CHECK_FAIL and \ref CYHAL_SYSPM_AFTER_TRANSITION.
345  * \note The callback will be executed from a critical section
346  *
347  * @param[in] callback_data The data for the callback to register
348  */
349 void cyhal_syspm_register_callback(cyhal_syspm_callback_data_t *callback_data);
350 
351 /** Removes the registered handler from the power manager so no future notifications are made.
352  *
353  * * @param[in] callback_data The data for the callback to unregister
354 */
355 void cyhal_syspm_unregister_callback(cyhal_syspm_callback_data_t *callback_data);
356 
357 /** Set CPU to sleep mode.
358  *
359  * @return  Returns CY_RSLT_SUCCESS if the processor successfully entered the sleep mode ,
360  * otherwise error.
361  */
362 cy_rslt_t cyhal_syspm_sleep(void);
363 
364 /** Set CPU to deep sleep mode.
365  *
366  * @return  Returns CY_RSLT_SUCCESS if the processor successfully entered the deep sleep mode,
367  * otherwise error.
368  */
369 cy_rslt_t cyhal_syspm_deepsleep(void);
370 
371 /** Lock deep sleep.
372  *
373  * This function prevents the CPU from going to deep sleep. The lock is implemented as a counter
374  * from 0 to USHRT_MAX. Each call to this function increments the counter by 1.
375  * \ref cyhal_syspm_unlock_deepsleep must be called an equal number of times to fully unlock.
376  * Deepsleep will only be allowed when the counter is equal to 0.
377  */
378 void cyhal_syspm_lock_deepsleep(void);
379 
380 /** Unlock deep sleep.
381  *
382  * This function decrements the lock counter by 1 and must be called an equal number of times as
383  * @ref cyhal_syspm_lock_deepsleep is called to fully unlock. Deepsleep will only be allowed
384  * when the counter is equal to 0.
385  */
386 void cyhal_syspm_unlock_deepsleep(void);
387 
388 /** Timed deep-sleep without system timer.
389  *
390  * Provides a way to deep-sleep for a desired number of milliseconds(ms) with the system timer disabled.
391  * The system timer is disabled before sleeping and a low power timer is setup to wake
392  * the device from deep-sleep after the desired number of ms have elapsed.
393  *
394  * @note The actual ms in the best case will be 1 ms less than the desired time to
395  * prevent the device from over-sleeping due to low clock accuracy.
396  *
397  * @param[in]   lptimer_obj Pre-Initialized LPTimer object.
398  * @param[in]   desired_ms  Desired number of ms to deep-sleep.
399  * @param[out]  actual_ms   Actual number of ms that was spent in deep-sleep.
400  *                          This value can range from 0 to desired_ms - 1
401  *                          depending on how long the device was able to deep-sleep.
402  * @return The status of the deep-sleep request.
403  */
404 cy_rslt_t cyhal_syspm_tickless_deepsleep(cyhal_lptimer_t *lptimer_obj, uint32_t desired_ms, uint32_t *actual_ms);
405 
406 /** Timed sleep without system timer.
407  *
408  * Provides a way to sleep for a desired number of milliseconds(ms) with the system timer disabled.
409  * The system timer is disabled before sleeping and a low power timer is setup to wake
410  * the device from sleep after the desired number of ms have elapsed.
411  *
412  * @note The actual ms in the best case will be 1 ms less than the desired time to
413  * prevent the device from over-sleeping due to low clock accuracy.
414  *
415  * @param[in]   lptimer_obj Pre-Initialized LPTimer object.
416  * @param[in]   desired_ms  Desired number of ms to sleep.
417  * @param[out]  actual_ms   Actual number of ms that was spent in sleep.
418  *                          This value can range from 0 to desired_ms - 1
419  *                          depending on how long the device was able to sleep.
420  * @return The status of the sleep request.
421  */
422 cy_rslt_t cyhal_syspm_tickless_sleep(cyhal_lptimer_t *lptimer_obj, uint32_t desired_ms, uint32_t *actual_ms);
423 
424 /** Indicates, that \ref cyhal_syspm_get_deepsleep_mode function is available in this version of HAL. */
425 #define CYHAL_API_AVAILABLE_SYSPM_GET_DEEPSLEEP_MODE
426 
427 /** Get current deep sleep mode.
428  *
429  * Provides a way to get the current deep sleep mode.
430  * @return The current deep sleep mode.
431  */
432 cyhal_syspm_system_deep_sleep_mode_t cyhal_syspm_get_deepsleep_mode(void);
433 
434 /** Informs the system of the current voltage level on the specified supply.
435   *
436   * This is generally expected to be set once at system startup, but it may be set repeatedly during
437   * runtime if operating conditions change.
438   * Once set, this value can be queried via \ref cyhal_syspm_get_supply_voltage.
439   *
440   * \note This only informs the system of the voltage level. It does not alter any of the device operating conditions.
441   *
442   * @param supply The supply whose voltage is being specified.
443   * @param mvolts The voltage level on the specified supply, in millivolts.
444   */
445 void cyhal_syspm_set_supply_voltage(cyhal_syspm_voltage_supply_t supply, uint32_t mvolts);
446 
447 /** Retrieves the current voltage level on the specified supply, as set in \ref cyhal_syspm_set_supply_voltage.
448   *
449   * \note This only returns the value provided to \ref cyhal_syspm_set_supply_voltage. It does not perform any
450   * measurements of the current supply level.
451   *
452   * @param supply The supply whose voltage is being specified.
453   * @return The voltage level on the specified supply, in millivolts. If the voltage has not been specified,
454   * returns 0.
455   */
456 uint32_t cyhal_syspm_get_supply_voltage(cyhal_syspm_voltage_supply_t supply);
457 
458 #if defined(__cplusplus)
459 }
460 #endif
461 
462 #ifdef CYHAL_SYSPM_IMPL_HEADER
463 #include CYHAL_SYSPM_IMPL_HEADER
464 #endif /* CYHAL_SYSTEM_IMPL_HEADER */
465 
466 /** \} group_hal_system */
467