Skip to content

Commit 93ae504

Browse files
authored
Merge pull request #11434 from mikeysklar/rp2350-alarm
Add sleep and alarms for RP2350 boards
2 parents f38f20d + 0a305ee commit 93ae504

10 files changed

Lines changed: 232 additions & 28 deletions

File tree

‎ports/raspberrypi/Makefile‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,17 @@ UF2_ID = 0xe48bff59
482482
# Double coprocessor is only available on the ARM core.
483483
DOUBLE_EABI = dcp
484484
INC += \
485-
-isystem sdk/src/rp2_common/hardware_dcp/include/
485+
-isystem sdk/src/rp2_common/hardware_dcp/include/ \
486+
-isystem sdk/src/rp2_common/hardware_rosc/include/ \
487+
-isystem sdk/src/rp2_common/pico_low_power/include/
486488

487489
CFLAGS += -DPICO_RP2040=0 -DPICO_RP2350=1
488490

489491
SRC_SDK_CHIP_VARIANT := \
492+
src/common/pico_util/fixed_bitset.c \
490493
src/rp2_common/hardware_powman/powman.c \
494+
src/rp2_common/hardware_rosc/rosc.c \
495+
src/rp2_common/pico_low_power/low_power.c \
491496

492497
SRC_S_UPPER_CHIP_VARIANT := \
493498
sdk/src/rp2_common/pico_double/double_conv_m33.S \

‎ports/raspberrypi/common-hal/alarm/__init__.c‎

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,21 @@
3434
#include "hardware/structs/watchdog.h"
3535

3636
// XOSC shutdown
37-
#include "hardware/rtc.h"
3837
#include "hardware/pll.h"
3938
#include "hardware/regs/io_bank0.h"
4039

40+
#if PICO_RP2350
41+
#include "hardware/powman.h"
42+
#include "pico/low_power.h"
43+
#endif
44+
4145
// Watchdog scratch register
4246
// Not used elsewhere in the SDK for now, keep an eye on it
4347
#define RP_WKUP_SCRATCH_REG 0
4448

4549
// Light sleep turns off nonvolatile Busio and other wake-only peripherals
4650
// TODO: this only saves about 2mA right now, expand with other non-essentials
51+
#if PICO_RP2040
4752
const uint32_t RP_LIGHTSLEEP_EN0_MASK = ~(
4853
CLOCKS_SLEEP_EN0_CLK_SYS_SPI1_BITS |
4954
CLOCKS_SLEEP_EN0_CLK_PERI_SPI1_BITS |
@@ -59,7 +64,28 @@ const uint32_t RP_LIGHTSLEEP_EN0_MASK = ~(
5964
);
6065
// This bank has the USB clocks in it, leave it for now
6166
const uint32_t RP_LIGHTSLEEP_EN1_MASK = CLOCKS_SLEEP_EN1_RESET;
67+
#else
68+
// Same peripherals as RP2040. RP2350 adds PIO2 and moves SPI to the second bank.
69+
const uint32_t RP_LIGHTSLEEP_EN0_MASK = ~(
70+
CLOCKS_SLEEP_EN0_CLK_SYS_PWM_BITS |
71+
CLOCKS_SLEEP_EN0_CLK_SYS_PIO2_BITS |
72+
CLOCKS_SLEEP_EN0_CLK_SYS_PIO1_BITS |
73+
CLOCKS_SLEEP_EN0_CLK_SYS_PIO0_BITS |
74+
CLOCKS_SLEEP_EN0_CLK_SYS_I2C1_BITS |
75+
CLOCKS_SLEEP_EN0_CLK_SYS_I2C0_BITS |
76+
CLOCKS_SLEEP_EN0_CLK_SYS_ADC_BITS |
77+
CLOCKS_SLEEP_EN0_CLK_ADC_BITS
78+
);
79+
// This bank has the USB clocks in it, only turn off SPI
80+
const uint32_t RP_LIGHTSLEEP_EN1_MASK = CLOCKS_SLEEP_EN1_RESET & ~(
81+
CLOCKS_SLEEP_EN1_CLK_SYS_SPI1_BITS |
82+
CLOCKS_SLEEP_EN1_CLK_PERI_SPI1_BITS |
83+
CLOCKS_SLEEP_EN1_CLK_SYS_SPI0_BITS |
84+
CLOCKS_SLEEP_EN1_CLK_PERI_SPI0_BITS
85+
);
86+
#endif
6287

88+
#if PICO_RP2040
6389
// Light sleeps used for TimeAlarm deep sleep turn off almost everything
6490
const uint32_t RP_LIGHTSLEEP_EN0_MASK_HARSH = (
6591
CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS |
@@ -68,6 +94,7 @@ const uint32_t RP_LIGHTSLEEP_EN0_MASK_HARSH = (
6894
const uint32_t RP_LIGHTSLEEP_EN1_MASK_HARSH = 0x0;
6995

7096
static void prepare_for_dormant_xosc(void);
97+
#endif
7198

7299
// Singleton instance of SleepMemory.
73100
const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
@@ -80,13 +107,29 @@ const alarm_sleep_memory_obj_t alarm_sleep_memory_obj = {
80107
// This object lives across VM instantiations, so none of these objects can contain references to the heap.
81108
alarm_wake_alarm_union_t alarm_wake_alarm;
82109

110+
#if PICO_RP2350
111+
// powman remembers what caused the last power up until the next one. Only report
112+
// it until the first alarm_reset() after boot.
113+
static bool powman_wakeup_reported;
114+
115+
bool alarm_woke_from_powman(void) {
116+
// powman is not reset by a watchdog reboot, so its registers would be stale.
117+
return (powman_hw->chip_reset & POWMAN_CHIP_RESET_HAD_SWCORE_PD_BITS) &&
118+
!watchdog_caused_reboot() &&
119+
(powman_hw->last_swcore_pwrup & (RP_POWMAN_PWRUP_GPIO_BITS | RP_POWMAN_PWRUP_ALARM_BITS));
120+
}
121+
#endif
122+
83123
void alarm_reset(void) {
84124
alarm_sleep_memory_reset();
85125
alarm_pin_pinalarm_reset();
86126
alarm_time_timealarm_reset();
87127

88128
// Reset the scratch source
89129
watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] = RP_SLEEP_WAKEUP_UNDEF;
130+
#if PICO_RP2350
131+
powman_wakeup_reported = true;
132+
#endif
90133
}
91134

92135
static uint8_t _get_wakeup_cause(void) {
@@ -102,6 +145,14 @@ static uint8_t _get_wakeup_cause(void) {
102145
if (watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] != RP_SLEEP_WAKEUP_UNDEF) {
103146
return watchdog_hw->scratch[RP_WKUP_SCRATCH_REG];
104147
}
148+
#if PICO_RP2350
149+
if (!powman_wakeup_reported && alarm_woke_from_powman()) {
150+
if (powman_hw->last_swcore_pwrup & RP_POWMAN_PWRUP_ALARM_BITS) {
151+
return RP_SLEEP_WAKEUP_RTC;
152+
}
153+
return RP_SLEEP_WAKEUP_GPIO;
154+
}
155+
#endif
105156
return RP_SLEEP_WAKEUP_UNDEF;
106157
}
107158

@@ -172,7 +223,7 @@ mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const mp_obj
172223
clocks_hw->sleep_en1 = RP_LIGHTSLEEP_EN1_MASK;
173224

174225
// Enable System Control Block (SCB) deep sleep
175-
scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
226+
scb_hw->scr |= ARM_CPU_PREFIXED(SCR_SLEEPDEEP_BITS);
176227

177228
__wfi();
178229
}
@@ -194,7 +245,41 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala
194245
_setup_sleep_alarms(true, n_alarms, alarms);
195246
}
196247

248+
#if PICO_RP2350
249+
// Power down with powman. Waking up reboots into main(), so this does not return.
250+
static void MP_NORETURN rp2350_enter_deep_sleep(void) {
251+
#if CIRCUITPY_CYW43
252+
cyw43_enter_deep_sleep();
253+
#endif
254+
255+
// pico_low_power arms one wakeup per call and leaves the others alone, so
256+
// arm every pin alarm first.
257+
alarm_pin_pinalarm_enable_powman_wakeups();
258+
259+
// A NULL power state keeps only the SRAM that holds .persistent_data powered.
260+
// That is where alarm.sleep_memory lives.
261+
uint pin_number;
262+
bool edge;
263+
bool value;
264+
if (!alarm_time_timealarm_is_set() &&
265+
alarm_pin_pinalarm_first_powman_wakeup(&pin_number, &edge, &value)) {
266+
low_power_pstate_until_gpio_pin_state(pin_number, edge, value, NULL, NULL);
267+
} else {
268+
// With no alarm at all, sleep until a time that never comes.
269+
uint64_t wakeup_ms = alarm_time_timealarm_is_set() ? alarm_time_timealarm_get_wakeup_ms() : INT64_MAX / 1000;
270+
low_power_pstate_until_aon_timer(from_us_since_boot(wakeup_ms * 1000), NULL, NULL);
271+
}
272+
273+
// Could not power down, most likely because an alarm already fired.
274+
watchdog_hw->scratch[RP_WKUP_SCRATCH_REG] = _get_wakeup_cause();
275+
reset_cpu();
276+
}
277+
#endif
278+
197279
void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) {
280+
#if PICO_RP2350
281+
rp2350_enter_deep_sleep();
282+
#else
198283
bool timealarm_set = alarm_time_timealarm_is_set();
199284

200285
#if CIRCUITPY_CYW43
@@ -223,12 +308,14 @@ void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) {
223308
// Just before reset, enable the pinalarm interrupt.
224309
alarm_pin_pinalarm_entering_deep_sleep();
225310
reset_cpu();
311+
#endif
226312
}
227313

228314
void common_hal_alarm_gc_collect(void) {
229315
gc_collect_ptr(shared_alarm_get_wake_alarm());
230316
}
231317

318+
#if PICO_RP2040
232319
static void prepare_for_dormant_xosc(void) {
233320
// TODO: add ROSC support with sleep_run_from_dormant_source when it's added to SDK
234321
uint src_hz = XOSC_MHZ * MHZ;
@@ -259,3 +346,4 @@ static void prepare_for_dormant_xosc(void) {
259346
pll_deinit(pll_sys);
260347
pll_deinit(pll_usb);
261348
}
349+
#endif

‎ports/raspberrypi/common-hal/alarm/__init__.h‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@
1616
#define RP_SLEEP_WAKEUP_GPIO 1
1717
#define RP_SLEEP_WAKEUP_RTC 2
1818

19+
// Bits in powman_hw->last_swcore_pwrup. Bit 0 is a chip reset, bits 1 to 4 are
20+
// the four GPIO wakeups and bit 6 is the timer alarm.
21+
#define RP_POWMAN_PWRUP_GPIO_LSB 1
22+
#define RP_POWMAN_PWRUP_GPIO_BITS 0x1e
23+
#define RP_POWMAN_PWRUP_ALARM_BITS 0x40
24+
25+
#if PICO_RP2350
26+
// Returns true when this boot is powman powering the core back up for a deep
27+
// sleep alarm. Returns false after any other kind of reset.
28+
bool alarm_woke_from_powman(void);
29+
#endif
30+
1931
typedef union {
2032
alarm_pin_pinalarm_obj_t pin_alarm;
2133
alarm_time_timealarm_obj_t time_alarm;

‎ports/raspberrypi/common-hal/alarm/pin/PinAlarm.c‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@
1515
#include "hardware/gpio.h"
1616
#include "hardware/structs/iobank0.h"
1717

18+
#if PICO_RP2350
19+
#include "hardware/powman.h"
20+
21+
// Deep sleep on RP2350 wakes through powman, which has four GPIO wakeup slots.
22+
// The alarm objects are gone by the time we enter deep sleep, so copy what we need.
23+
#define POWMAN_WAKEUP_SLOTS (count_of(powman_hw->pwrup))
24+
typedef struct {
25+
uint8_t pin_number;
26+
bool edge;
27+
bool value;
28+
} powman_wakeup_t;
29+
static powman_wakeup_t powman_wakeups[POWMAN_WAKEUP_SLOTS];
30+
static size_t powman_wakeup_count;
31+
#endif
32+
1833
static bool woke_up;
1934
static uint64_t alarm_triggered_pins; // 36 actual pins
2035
static uint64_t alarm_reserved_pins; // 36 actual pins
@@ -89,6 +104,18 @@ mp_obj_t alarm_pin_pinalarm_record_wake_alarm(void) {
89104
alarm->base.type = &alarm_pin_pinalarm_type;
90105
// TODO: how to obtain the correct pin from memory?
91106
alarm->pin = NULL;
107+
#if PICO_RP2350
108+
// After a powman wake, the wakeup slot that fired still holds its pin number.
109+
if (!woke_up && alarm_woke_from_powman()) {
110+
uint32_t pwrup = powman_hw->last_swcore_pwrup & RP_POWMAN_PWRUP_GPIO_BITS;
111+
for (size_t i = 0; i < POWMAN_WAKEUP_SLOTS; i++) {
112+
if (pwrup & (1u << (i + RP_POWMAN_PWRUP_GPIO_LSB))) {
113+
alarm->pin = mcu_get_pin_by_number(powman_hw->pwrup[i] & POWMAN_PWRUP0_SOURCE_BITS);
114+
break;
115+
}
116+
}
117+
}
118+
#endif
92119
return alarm;
93120
}
94121

@@ -109,13 +136,35 @@ void alarm_pin_pinalarm_reset(void) {
109136
}
110137
}
111138
alarm_reserved_pins = 0;
139+
140+
#if PICO_RP2350
141+
for (size_t i = 0; i < POWMAN_WAKEUP_SLOTS; i++) {
142+
powman_disable_gpio_wakeup(i);
143+
}
144+
powman_wakeup_count = 0;
145+
#endif
112146
}
113147

114148
void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms) {
149+
#if PICO_RP2350
150+
powman_wakeup_count = 0;
151+
#endif
115152
for (size_t i = 0; i < n_alarms; i++) {
116153
if (mp_obj_is_type(alarms[i], &alarm_pin_pinalarm_type)) {
117154
alarm_pin_pinalarm_obj_t *alarm = MP_OBJ_TO_PTR(alarms[i]);
118155

156+
#if PICO_RP2350
157+
if (deep_sleep) {
158+
if (powman_wakeup_count >= POWMAN_WAKEUP_SLOTS) {
159+
mp_raise_ValueError_varg(MP_ERROR_TEXT("Too many %q"), MP_QSTR_PinAlarm);
160+
}
161+
powman_wakeups[powman_wakeup_count].pin_number = alarm->pin->number;
162+
powman_wakeups[powman_wakeup_count].edge = alarm->edge;
163+
powman_wakeups[powman_wakeup_count].value = alarm->value;
164+
powman_wakeup_count++;
165+
}
166+
#endif
167+
119168
gpio_init(alarm->pin->number);
120169
if (alarm->pull) {
121170
// If value is high, the pullup should be off, and vice versa
@@ -149,3 +198,24 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob
149198
}
150199
}
151200
}
201+
202+
#if PICO_RP2350
203+
// Arm the powman GPIO wakeups recorded by alarm_pin_pinalarm_set_alarms().
204+
// Call this just before powering down for deep sleep.
205+
void alarm_pin_pinalarm_enable_powman_wakeups(void) {
206+
for (size_t i = 0; i < powman_wakeup_count; i++) {
207+
powman_enable_gpio_wakeup(i, powman_wakeups[i].pin_number,
208+
powman_wakeups[i].edge, powman_wakeups[i].value);
209+
}
210+
}
211+
212+
bool alarm_pin_pinalarm_first_powman_wakeup(uint *pin_number, bool *edge, bool *value) {
213+
if (powman_wakeup_count == 0) {
214+
return false;
215+
}
216+
*pin_number = powman_wakeups[0].pin_number;
217+
*edge = powman_wakeups[0].edge;
218+
*value = powman_wakeups[0].value;
219+
return true;
220+
}
221+
#endif

‎ports/raspberrypi/common-hal/alarm/pin/PinAlarm.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,11 @@ void alarm_pin_pinalarm_light_reset(void);
2727
void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_obj_t *alarms);
2828
bool alarm_pin_pinalarm_woke_this_cycle(void);
2929
void alarm_pin_pinalarm_entering_deep_sleep(void);
30+
#if PICO_RP2350
31+
// Arms the powman GPIO wakeups for the pin alarms given to
32+
// alarm_pin_pinalarm_set_alarms() with deep_sleep true.
33+
void alarm_pin_pinalarm_enable_powman_wakeups(void);
34+
// Returns false and leaves the outputs alone when no pin alarm was set for deep sleep.
35+
// Otherwise fills in the pin alarm that uses powman wakeup slot 0 and returns true.
36+
bool alarm_pin_pinalarm_first_powman_wakeup(uint *pin_number, bool *edge, bool *value);
37+
#endif

0 commit comments

Comments
 (0)