1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <inttypes.h>
10 
11 #include "pico.h"
12 
13 #include "hardware/gpio.h"
14 #include "hardware/powman.h"
15 
16 #ifndef PICO_POWMAN_DEBUG
17 #define PICO_POWMAN_DEBUG 0
18 #endif
19 
20 #if PICO_POWMAN_DEBUG
21 bool powman_debug_printf = false;
powman_enable_debug_printf(void)22 void powman_enable_debug_printf(void) {
23     powman_debug_printf = true;
24 }
25 #define powman_debug(format, args...) if (powman_debug_printf) printf(format, ## args)
26 #else
27 #define powman_debug(...)
28 #endif
29 
powman_write(volatile uint32_t * reg,uint32_t value)30 static inline void powman_write(volatile uint32_t *reg, uint32_t value) {
31     // Write needs a password in top 16 bits
32     invalid_params_if(HARDWARE_POWMAN, value >> 16);
33     *reg = POWMAN_PASSWORD_BITS | value;
34 }
35 
powman_timer_set_ms(uint64_t time_ms)36 void powman_timer_set_ms(uint64_t time_ms) {
37     bool was_running = powman_timer_is_running();
38     if (was_running) powman_timer_stop();
39     powman_write(&powman_hw->set_time_15to0, time_ms & 0xffff);
40     powman_write(&powman_hw->set_time_31to16, (time_ms >> 16) & 0xffff);
41     powman_write(&powman_hw->set_time_47to32, (time_ms >> 32) & 0xffff);
42     powman_write(&powman_hw->set_time_63to48, (time_ms >> 48) & 0xffff);
43     if (was_running) powman_timer_start();
44 }
45 
powman_timer_get_ms(void)46 uint64_t powman_timer_get_ms(void) {
47     // Need to make sure that the upper 32 bits of the timer
48     // don't change, so read that first
49     uint32_t hi = powman_hw->read_time_upper;
50     uint32_t lo;
51     do {
52         // Read the lower 32 bits
53         lo = powman_hw->read_time_lower;
54         // Now read the upper 32 bits again and
55         // check that it hasn't incremented. If it has loop around
56         // and read the lower 32 bits again to get an accurate value
57         uint32_t next_hi = powman_hw->read_time_upper;
58         if (hi == next_hi) break;
59         hi = next_hi;
60     } while (true);
61     return ((uint64_t) hi << 32u) | lo;
62 }
63 
powman_timer_set_1khz_tick_source_lposc(void)64 void powman_timer_set_1khz_tick_source_lposc(void) {
65     powman_timer_set_1khz_tick_source_lposc_with_hz(32768);
66 }
67 
powman_timer_set_1khz_tick_source_lposc_with_hz(uint32_t lposc_freq_hz)68 void powman_timer_set_1khz_tick_source_lposc_with_hz(uint32_t lposc_freq_hz) {
69     bool was_running = powman_timer_is_running();
70     if (was_running) powman_timer_stop();
71     uint32_t lposc_freq_khz = lposc_freq_hz / 1000;
72     uint32_t lposc_freq_khz_frac16 = (lposc_freq_khz % 1000) * 65536 / 1000;
73     powman_write(&powman_hw->lposc_freq_khz_int, lposc_freq_khz);
74     powman_write(&powman_hw->lposc_freq_khz_frac, lposc_freq_khz_frac16);
75     powman_set_bits(&powman_hw->timer, POWMAN_TIMER_USE_LPOSC_BITS);
76     if (was_running) {
77         powman_timer_start();
78         while(!(powman_hw->timer & POWMAN_TIMER_USING_LPOSC_BITS));
79     }
80 }
81 
powman_timer_set_1khz_tick_source_xosc(void)82 void powman_timer_set_1khz_tick_source_xosc(void) {
83     powman_timer_set_1khz_tick_source_xosc_with_hz(XOSC_HZ);
84 }
85 
powman_timer_set_1khz_tick_source_xosc_with_hz(uint32_t xosc_freq_hz)86 void powman_timer_set_1khz_tick_source_xosc_with_hz(uint32_t xosc_freq_hz) {
87     bool was_running = powman_timer_is_running();
88     if (was_running) powman_timer_stop();
89     uint32_t xosc_freq_khz = xosc_freq_hz / 1000;
90     uint32_t xosc_freq_khz_frac16 = (xosc_freq_khz % 1000) * 65536 / 1000;
91     powman_write(&powman_hw->xosc_freq_khz_int, xosc_freq_khz);
92     powman_write(&powman_hw->xosc_freq_khz_frac, xosc_freq_khz_frac16);
93     powman_set_bits(&powman_hw->timer, POWMAN_TIMER_USE_XOSC_BITS);
94     if (was_running) {
95         powman_timer_start();
96         while(!(powman_hw->timer & POWMAN_TIMER_USING_XOSC_BITS));
97     }
98 }
99 
powman_timer_use_gpio(uint32_t gpio,uint32_t use,uint32_t using)100 static void powman_timer_use_gpio(uint32_t gpio, uint32_t use, uint32_t using) {
101     bool was_running = powman_timer_is_running();
102     if (was_running) powman_timer_stop();
103     invalid_params_if(HARDWARE_POWMAN, !((gpio == 12) || (gpio == 14) || (gpio == 20) || (gpio == 22)));
104     gpio_set_input_enabled(gpio, true);
105     powman_write(&powman_hw->ext_time_ref, gpio);
106     powman_set_bits(&powman_hw->timer, use);
107     if (was_running) {
108         powman_timer_start();
109         while(!(powman_hw->timer & using));
110     }
111 }
112 
powman_timer_set_1khz_tick_source_gpio(uint32_t gpio)113 void powman_timer_set_1khz_tick_source_gpio(uint32_t gpio) {
114     // todo check if we're using the GPIO setup already?
115     powman_timer_use_gpio(gpio, POWMAN_TIMER_USE_GPIO_1KHZ_BITS, POWMAN_TIMER_USING_GPIO_1KHZ_BITS);
116 }
117 
powman_timer_enable_gpio_1hz_sync(uint32_t gpio)118 void powman_timer_enable_gpio_1hz_sync(uint32_t gpio) {
119     // todo check if we're using the GPIO setup already?
120     powman_timer_use_gpio(gpio, POWMAN_TIMER_USE_GPIO_1HZ_BITS, POWMAN_TIMER_USING_GPIO_1HZ_BITS);
121 }
122 
powman_timer_disable_gpio_1hz_sync(void)123 void powman_timer_disable_gpio_1hz_sync(void) {
124     powman_clear_bits(&powman_hw->timer, POWMAN_TIMER_USE_GPIO_1HZ_BITS);
125 }
126 
powman_get_power_state(void)127 powman_power_state powman_get_power_state(void) {
128     uint32_t state_reg = powman_hw->state & POWMAN_STATE_CURRENT_BITS;
129     // todo we should have hardware/regs/powman.h values for these
130     static_assert(POWMAN_POWER_DOMAIN_SRAM_BANK1 == 0, "");
131     static_assert(POWMAN_POWER_DOMAIN_SRAM_BANK0 == 1, "");
132     static_assert(POWMAN_POWER_DOMAIN_XIP_CACHE == 2, "");
133     static_assert(POWMAN_POWER_DOMAIN_SWITCHED_CORE == 3, "");
134     static_assert(POWMAN_STATE_CURRENT_BITS == 0xf, "");
135     return (powman_power_state) state_reg;
136 }
137 
138 // TODO: Should this fail to go to sleep if there is no wakeup alarm
powman_set_power_state(powman_power_state state)139 int powman_set_power_state(powman_power_state state) {
140     // Clear req ignored in case it has been set
141     powman_clear_bits(&powman_hw->state, POWMAN_STATE_REQ_IGNORED_BITS);
142     powman_debug("powman: Requesting state %x\n", state);
143     powman_write(&powman_hw->state, (~state << POWMAN_STATE_REQ_LSB) & POWMAN_STATE_REQ_BITS);
144 
145     // Has it been ignored?
146     if (powman_hw->state & POWMAN_STATE_REQ_IGNORED_BITS) {
147         powman_debug("State req ignored because of a pending pwrup req: %"PRIx32"\n", powman_hw->current_pwrup_req);
148         return PICO_ERROR_PRECONDITION_NOT_MET;
149     }
150 
151     bool state_valid = (powman_hw->state & POWMAN_STATE_BAD_SW_REQ_BITS) == 0;
152     if (!state_valid) {
153         powman_debug("powman: Requested state invalid\n");
154         return PICO_ERROR_INVALID_ARG;
155     } else {
156         powman_debug("powman: Requested state valid\n");
157     }
158     if (!powman_power_state_is_domain_on(state, POWMAN_POWER_DOMAIN_SWITCHED_CORE)) {
159         // If we are turning off switched core then POWMAN_STATE_WAITING_BITS will be
160         // set because we are waiting for proc to go to sleep, so return ok and then the proc
161         // can go to sleep
162 
163         // Note if the powerdown is being blocked by a pending pwrup request we will break out of this and return a failure
164 
165         // Clk pow is slow so can take a few clk_pow cycles for waiting to turn up
166         for (int i = 0; i < 100; i++) {
167             if (powman_hw->state & POWMAN_STATE_WAITING_BITS) {
168                 return PICO_OK;
169             }
170         }
171 
172         // If it hasn't turned up then false
173         powman_debug("powman: STATE_WAITING hasn't turned up\n");
174         return PICO_ERROR_TIMEOUT;
175     }
176     // Wait while the state is changing then return true as we will be in the new state
177     powman_debug("powman: waiting for state change\n");
178     while(powman_hw->state & POWMAN_STATE_CHANGING_BITS) tight_loop_contents();
179     powman_debug("powman: state changed to %x\n", state);
180     return PICO_OK;
181 }
182 
powman_configure_wakeup_state(powman_power_state sleep_state,powman_power_state wakeup_state)183 bool powman_configure_wakeup_state(powman_power_state sleep_state, powman_power_state wakeup_state) {
184     // When powman wakes up it can keep the state of the sram0 and sram1 banks. Note, it can't
185     // explicitly
186     bool valid = powman_power_state_is_domain_on(wakeup_state, POWMAN_POWER_DOMAIN_XIP_CACHE);
187     valid &= powman_power_state_is_domain_on(wakeup_state, POWMAN_POWER_DOMAIN_SWITCHED_CORE);
188     valid &= powman_power_state_is_domain_on(sleep_state, POWMAN_POWER_DOMAIN_SRAM_BANK0) ==
189              powman_power_state_is_domain_on(wakeup_state, POWMAN_POWER_DOMAIN_SRAM_BANK0);
190     valid &= powman_power_state_is_domain_on(sleep_state, POWMAN_POWER_DOMAIN_SRAM_BANK1) ==
191              powman_power_state_is_domain_on(wakeup_state, POWMAN_POWER_DOMAIN_SRAM_BANK1);
192     if (valid) {
193         powman_clear_bits(&powman_hw->seq_cfg, POWMAN_SEQ_CFG_HW_PWRUP_SRAM0_BITS | POWMAN_SEQ_CFG_HW_PWRUP_SRAM1_BITS);
194         uint32_t seq_cfg_set = 0;
195         if (!powman_power_state_is_domain_on(sleep_state, POWMAN_POWER_DOMAIN_SRAM_BANK0)) seq_cfg_set |= POWMAN_SEQ_CFG_HW_PWRUP_SRAM0_BITS;
196         if (!powman_power_state_is_domain_on(sleep_state, POWMAN_POWER_DOMAIN_SRAM_BANK1)) seq_cfg_set |= POWMAN_SEQ_CFG_HW_PWRUP_SRAM1_BITS;
197         powman_set_bits(&powman_hw->seq_cfg, seq_cfg_set);
198     }
199     return valid;
200 }
201 
powman_timer_enable_alarm_at_ms(uint64_t alarm_time_ms)202 void powman_timer_enable_alarm_at_ms(uint64_t alarm_time_ms) {
203     powman_set_bits(&powman_hw->inte, POWMAN_INTE_TIMER_BITS);
204     powman_clear_bits(&powman_hw->timer, POWMAN_TIMER_ALARM_ENAB_BITS);
205     // Alarm must be disabled to set the alarm time
206     powman_write(&powman_hw->alarm_time_15to0, alarm_time_ms & 0xffff);
207     powman_write(&powman_hw->alarm_time_31to16, (alarm_time_ms >> 16) & 0xffff);
208     powman_write(&powman_hw->alarm_time_47to32, (alarm_time_ms >> 32) & 0xffff);
209     powman_write(&powman_hw->alarm_time_63to48, (alarm_time_ms >> 48) & 0xffff);
210     powman_clear_alarm();
211     // TODO: Assuming pwrup on alarm has no bad side effects if already powered up
212     powman_set_bits(&powman_hw->timer, POWMAN_TIMER_ALARM_ENAB_BITS);
213 }
214 
powman_timer_disable_alarm(void)215 void powman_timer_disable_alarm(void) {
216     powman_clear_bits(&powman_hw->inte, POWMAN_INTE_TIMER_BITS);
217     powman_clear_bits(&powman_hw->timer, POWMAN_TIMER_ALARM_ENAB_BITS);
218 }
219 
powman_enable_alarm_wakeup_at_ms(uint64_t alarm_time_ms)220 void powman_enable_alarm_wakeup_at_ms(uint64_t alarm_time_ms) {
221     powman_timer_enable_alarm_at_ms(alarm_time_ms);
222     powman_set_bits(&powman_hw->timer, POWMAN_TIMER_PWRUP_ON_ALARM_BITS);
223 }
224 
powman_disable_alarm_wakeup(void)225 void powman_disable_alarm_wakeup(void) {
226     powman_timer_disable_alarm();
227     powman_clear_bits(&powman_hw->timer, POWMAN_TIMER_PWRUP_ON_ALARM_BITS);
228 }
229 
powman_enable_gpio_wakeup(uint gpio_wakeup_num,uint32_t gpio,bool edge,bool high)230 void powman_enable_gpio_wakeup(uint gpio_wakeup_num, uint32_t gpio, bool edge, bool high) {
231     invalid_params_if(HARDWARE_POWMAN, gpio_wakeup_num >= count_of(powman_hw->pwrup));
232 
233     // Need to make sure pad is input enabled
234     gpio_set_input_enabled(gpio, true);
235 
236     // Set up gpio hardware for what we want
237     uint32_t pwrup = (edge ? POWMAN_PWRUP0_MODE_VALUE_EDGE : POWMAN_PWRUP0_MODE_VALUE_LEVEL) << POWMAN_PWRUP0_MODE_LSB;
238     pwrup |= (high ? POWMAN_PWRUP0_DIRECTION_BITS : 0);
239     pwrup |= gpio << POWMAN_PWRUP0_SOURCE_LSB;
240     powman_write(&powman_hw->pwrup[gpio_wakeup_num], pwrup);
241 
242     // Clear the status bit in case an edge is already latched
243     powman_clear_bits(&powman_hw->pwrup[gpio_wakeup_num], POWMAN_PWRUP0_STATUS_BITS);
244 
245     // Important to enable it separately to allow the gpio to change
246     powman_set_bits(&powman_hw->pwrup[gpio_wakeup_num], POWMAN_PWRUP0_ENABLE_BITS);
247 }
248 
powman_disable_gpio_wakeup(uint gpio_wakeup_num)249 void powman_disable_gpio_wakeup(uint gpio_wakeup_num) {
250     invalid_params_if(HARDWARE_POWMAN, gpio_wakeup_num >= count_of(powman_hw->pwrup));
251     powman_clear_bits(&powman_hw->pwrup[gpio_wakeup_num], POWMAN_PWRUP0_ENABLE_BITS);
252 }
253 
powman_disable_all_wakeups(void)254 void powman_disable_all_wakeups(void) {
255     for (uint i = 0; i < count_of(powman_hw->pwrup); i++) {
256         powman_disable_gpio_wakeup(i);
257     }
258     powman_disable_alarm_wakeup();
259 }
260