1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _HARDWARE_POWMAN_H
8 #define _HARDWARE_POWMAN_H
9 
10 #include "pico.h"
11 #include "hardware/structs/powman.h"
12 
13 /** \file hardware/powman.h
14  *  \defgroup hardware_powman hardware_powman
15  *
16  * \brief Power Management API
17  *
18  */
19 
20 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_POWMAN, Enable/disable hardware_powman assertions, type=bool, default=0, group=hardware_powman
21 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_POWMAN
22 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_POWMAN 0
23 #endif
24 
25 /*! \brief Use the ~32KHz low power oscillator as the powman timer source
26  *  \ingroup hardware_powman
27  */
28 void powman_timer_set_1khz_tick_source_lposc(void);
29 
30 /*! \brief Use the low power oscillator (specifying frequency) as the powman timer source
31  *  \ingroup hardware_powman
32  *  \param lposc_freq_hz specify an exact lposc freq to trim it
33  */
34 void powman_timer_set_1khz_tick_source_lposc_with_hz(uint32_t lposc_freq_hz);
35 
36 /*! \brief Use the crystal oscillator as the powman timer source
37  *  \ingroup hardware_powman
38  */
39 void powman_timer_set_1khz_tick_source_xosc(void);
40 
41 /*! \brief Use the crystal oscillator as the powman timer source
42  *  \ingroup hardware_powman
43  *  \param xosc_freq_hz specify a crystal frequency
44  */
45 void powman_timer_set_1khz_tick_source_xosc_with_hz(uint32_t xosc_freq_hz);
46 
47 /*! \brief Use a 1KHz external tick as the powman timer source
48  *  \ingroup hardware_powman
49  *  \param gpio the gpio to use. must be 12, 14, 20, 22
50  */
51 void powman_timer_set_1khz_tick_source_gpio(uint32_t gpio);
52 
53 /*! \brief Use a 1Hz external signal as the powman timer source for seconds only
54  *  \ingroup hardware_powman
55  *
56  * Use a 1hz sync signal, such as from a gps for the seconds component of the timer.
57  * The milliseconds will still come from another configured source such as xosc or lposc
58  *
59  * \param gpio the gpio to use. must be 12, 14, 20, 22
60  */
61 void powman_timer_enable_gpio_1hz_sync(uint32_t gpio);
62 
63 /*! \brief Stop using 1Hz external signal as the powman timer source for seconds
64  *  \ingroup hardware_powman
65  */
66 void powman_timer_disable_gpio_1hz_sync(void);
67 
68 /*! \brief Returns current time in ms
69  *  \ingroup hardware_powman
70  */
71 uint64_t powman_timer_get_ms(void);
72 
73 /*! \brief Set current time in ms
74  *  \ingroup hardware_powman
75  *
76  * \param time_ms Current time in ms
77  */
78 void powman_timer_set_ms(uint64_t time_ms);
79 
80 /*! \brief Set an alarm at an absolute time in ms
81  *  \ingroup hardware_powman
82  *
83  * Note, the timer is stopped and then restarted as part of this function. This only controls the alarm
84  * if you want to use the alarm to wake up powman then you should use \ref powman_enable_alarm_wakeup_at_ms
85  *
86  * \param alarm_time_ms time at which the alarm will fire
87  */
88 void powman_timer_enable_alarm_at_ms(uint64_t alarm_time_ms);
89 
90 /*! \brief Disable the alarm
91  *  \ingroup hardware_powman
92  *
93  * Once an alarm has fired it must be disabled to stop firing as the alarm
94  * comparison is alarm = alarm_time >= current_time
95  */
96 void powman_timer_disable_alarm(void);
97 
98 /*! \brief hw_set_bits helper function
99  *  \ingroup hardware_powman
100  *
101  * \param reg register to set
102  * \param bits bits of register to set
103  * Powman needs a password for writes, to prevent accidentally writing to it.
104  * This function implements hw_set_bits with an appropriate password.
105  */
powman_set_bits(volatile uint32_t * reg,uint32_t bits)106 static inline void powman_set_bits(volatile uint32_t *reg, uint32_t bits) {
107     invalid_params_if(HARDWARE_POWMAN, bits >> 16);
108     hw_set_bits(reg, POWMAN_PASSWORD_BITS | bits);
109 }
110 
111 /*! \brief hw_clear_bits helper function
112  *  \ingroup hardware_powman
113  *
114  * Powman needs a password for writes, to prevent accidentally writing to it.
115  * This function implements hw_clear_bits with an appropriate password.
116  *
117  * \param reg register to clear
118  * \param bits bits of register to clear
119  */
powman_clear_bits(volatile uint32_t * reg,uint32_t bits)120 static inline void powman_clear_bits(volatile uint32_t *reg, uint32_t bits) {
121     invalid_params_if(HARDWARE_POWMAN, bits >> 16);
122     hw_clear_bits(reg, POWMAN_PASSWORD_BITS | bits);
123 }
124 
125 /*! \brief Determine if the powman timer is running
126  *  \ingroup hardware_powman
127  */
powman_timer_is_running(void)128 static inline bool powman_timer_is_running(void) {
129     return powman_hw->timer & POWMAN_TIMER_RUN_BITS;
130 }
131 
132 /*! \brief Stop the powman timer
133  * \ingroup hardware_powman
134  */
powman_timer_stop(void)135 static inline void powman_timer_stop(void) {
136     powman_clear_bits(&powman_hw->timer, POWMAN_TIMER_RUN_BITS);
137 }
138 
139 /*! \brief Start the powman timer
140  * \ingroup hardware_powman
141  */
powman_timer_start(void)142 static inline void powman_timer_start(void) {
143     powman_set_bits(&powman_hw->timer, POWMAN_TIMER_RUN_BITS);
144 }
145 
146 /*! \brief Clears the powman alarm
147  * \ingroup hardware_powman
148  *
149  * Note, the alarm must be disabled (see \ref powman_timer_disable_alarm) before clearing the alarm, as the alarm fires if
150  * the time is greater than equal to the target, so once the time has passed the alarm will always fire while enabled.
151  */
powman_clear_alarm(void)152 static inline void powman_clear_alarm(void) {
153     powman_clear_bits(&powman_hw->timer, POWMAN_TIMER_ALARM_BITS);
154 }
155 
156 /*! \brief Power domains of powman
157  *  \ingroup hardware_powman
158  */
159 enum powman_power_domains {
160     POWMAN_POWER_DOMAIN_SRAM_BANK1 = 0,    ///< bank1 includes the top 256K of sram plus sram 8 and 9 (scratch x and scratch y)
161     POWMAN_POWER_DOMAIN_SRAM_BANK0 = 1,    ///< bank0 is bottom 256K of sSRAM
162     POWMAN_POWER_DOMAIN_XIP_CACHE = 2,     ///< XIP cache is 2x8K instances
163     POWMAN_POWER_DOMAIN_SWITCHED_CORE = 3, ///< Switched core logic (processors, busfabric, peris etc)
164     POWMAN_POWER_DOMAIN_COUNT = 4,
165 };
166 
167 typedef uint32_t powman_power_state;
168 
169 /*! \brief Get the current power state
170  *  \ingroup hardware_powman
171  */
172 powman_power_state powman_get_power_state(void);
173 
174 /*! \brief Set the power state
175  * \ingroup hardware_powman
176  *
177  * Check the desired state is valid. Powman will go to the state if it is valid and there are no pending power up requests.
178  *
179  * Note that if you are turning off the switched core then this function will never return as the processor will have
180  * been turned off at the end.
181  *
182  * \param state the power state to go to
183  * \returns PICO_OK if the state is valid. Misc PICO_ERRORs are returned if not
184  */
185 int powman_set_power_state(powman_power_state state);
186 
187 #define POWMAN_POWER_STATE_NONE 0
188 
189 /*! \brief Helper function modify a powman_power_state to turn a domain on
190  * \ingroup hardware_powman
191  * \param orig original state
192  * \param domain domain to turn on
193  */
powman_power_state_with_domain_on(powman_power_state orig,enum powman_power_domains domain)194 static inline powman_power_state powman_power_state_with_domain_on(powman_power_state orig, enum powman_power_domains domain) {
195     invalid_params_if(HARDWARE_POWMAN, domain >= POWMAN_POWER_DOMAIN_COUNT);
196     return orig | (1u << domain);
197 }
198 
199 /*! \brief Helper function modify a powman_power_state to turn a domain off
200  * \ingroup hardware_powman
201  * \param orig original state
202  * \param domain domain to turn off
203  */
powman_power_state_with_domain_off(powman_power_state orig,enum powman_power_domains domain)204 static inline powman_power_state powman_power_state_with_domain_off(powman_power_state orig, enum powman_power_domains domain) {
205     invalid_params_if(HARDWARE_POWMAN, domain >= POWMAN_POWER_DOMAIN_COUNT);
206     return orig &= ~(1u << domain);
207 }
208 
209 /*! \brief Helper function to check if a domain is on in a given powman_power_state
210  * \ingroup hardware_powman
211  * \param state powman_power_state
212  * \param domain domain to check is on
213  */
powman_power_state_is_domain_on(powman_power_state state,enum powman_power_domains domain)214 static inline bool powman_power_state_is_domain_on(powman_power_state state, enum powman_power_domains domain) {
215     invalid_params_if(HARDWARE_POWMAN, domain >= POWMAN_POWER_DOMAIN_COUNT);
216     return state & (1u << domain);
217 }
218 
219 /*! \brief Wake up from an alarm at a given time
220  * \ingroup hardware_powman
221  * \param alarm_time_ms time to wake up in ms
222  */
223 void powman_enable_alarm_wakeup_at_ms(uint64_t alarm_time_ms);
224 
225 /*! \brief Wake up from a gpio
226  * \ingroup hardware_powman
227  * \param gpio_wakeup_num hardware wakeup instance to use (0-3)
228  * \param gpio gpio to wake up from (0-47)
229  * \param edge true for edge sensitive, false for level sensitive
230  * \param high true for active high, false active low
231  */
232 void powman_enable_gpio_wakeup(uint gpio_wakeup_num, uint32_t gpio, bool edge, bool high);
233 
234 /*! \brief Disable waking up from alarm
235  *  \ingroup hardware_powman
236  */
237 void powman_disable_alarm_wakeup(void);
238 
239 /*! \brief Disable wake up from a gpio
240  * \ingroup hardware_powman
241  * \param gpio_wakeup_num hardware wakeup instance to use (0-3)
242  */
243 void powman_disable_gpio_wakeup(uint gpio_wakeup_num);
244 
245 /*! \brief Disable all wakeup sources
246  *  \ingroup hardware_powman
247  */
248 void powman_disable_all_wakeups(void);
249 
250 /*! \brief Configure sleep state and wakeup state
251  * \ingroup hardware_powman
252  * \param sleep_state power state powman will go to when sleeping, used to validate the wakeup state
253  * \param wakeup_state power state powman will go to when waking up. Note switched core and xip always power up. SRAM bank0 and bank1 can be left powered off
254  * \returns true if the state is valid, false if not
255  */
256 bool powman_configure_wakeup_state(powman_power_state sleep_state, powman_power_state wakeup_state);
257 
258 /*! \brief Ignore wake up when the debugger is attached
259  *  \ingroup hardware_powman
260  *
261  * Typically, when a debugger is attached it will assert the pwrupreq signal. OpenOCD does not clear this signal, even when you quit.
262  * This means once you have attached a debugger powman will never go to sleep. This function lets you ignore the debugger
263  * pwrupreq which means you can go to sleep with a debugger attached. The debugger will error out if you go to turn off the switch core with it attached,
264  * as the processors have been powered off.
265  *
266  * \param ignored should the debugger power up request be ignored
267  */
powman_set_debug_power_request_ignored(bool ignored)268 static inline void powman_set_debug_power_request_ignored(bool ignored) {
269     if (ignored)
270         powman_set_bits(&powman_hw->dbg_pwrcfg, 1);
271     else
272         powman_clear_bits(&powman_hw->dbg_pwrcfg, 0);
273 }
274 
275 #endif