First version of a DeepSleep state for the RP2040 (#4976)

* Adding pico-extra utils

* RP2040 can now go to deepsleep

* First RP2040 DeepSleep code - TODO : do better and restore

* FIX RAK11310 compilation (revert SDK + missing defines)

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
TheMalkavien
2024-10-08 05:24:37 -05:00
committed by GitHub
co-authored by GitHub Ben Meadors
parent 2e5399dbe4
commit a0dd7b43d5
11 changed files with 479 additions and 9 deletions
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_ROSC_H_
#define _HARDWARE_ROSC_H_
#include "pico.h"
#include "hardware/structs/rosc.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file rosc.h
* \defgroup hardware_rosc hardware_rosc
*
* Ring Oscillator (ROSC) API
*
* A Ring Oscillator is an on-chip oscillator that requires no external crystal. Instead, the output is generated from a series of
* inverters that are chained together to create a feedback loop. RP2040 boots from the ring oscillator initially, meaning the
* first stages of the bootrom, including booting from SPI flash, will be clocked by the ring oscillator. If your design has a
* crystal oscillator, youll likely want to switch to this as your reference clock as soon as possible, because the frequency is
* more accurate than the ring oscillator.
*/
/*! \brief Set frequency of the Ring Oscillator
* \ingroup hardware_rosc
*
* \param code The drive strengths. See the RP2040 datasheet for information on this value.
*/
void rosc_set_freq(uint32_t code);
/*! \brief Set range of the Ring Oscillator
* \ingroup hardware_rosc
*
* Frequency range. Frequencies will vary with Process, Voltage & Temperature (PVT).
* Clock output will not glitch when changing the range up one step at a time.
*
* \param range 0x01 Low, 0x02 Medium, 0x03 High, 0x04 Too High.
*/
void rosc_set_range(uint range);
/*! \brief Disable the Ring Oscillator
* \ingroup hardware_rosc
*
*/
void rosc_disable(void);
/*! \brief Put Ring Oscillator in to dormant mode.
* \ingroup hardware_rosc
*
* The ROSC supports a dormant mode,which stops oscillation until woken up up by an asynchronous interrupt.
* This can either come from the RTC, being clocked by an external clock, or a GPIO pin going high or low.
* If no IRQ is configured before going into dormant mode the ROSC will never restart.
*
* PLLs should be stopped before selecting dormant mode.
*/
void rosc_set_dormant(void);
// FIXME: Add doxygen
uint32_t next_rosc_code(uint32_t code);
uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz);
void rosc_set_div(uint32_t div);
inline static void rosc_clear_bad_write(void) {
hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS);
}
inline static bool rosc_write_okay(void) {
return !(rosc_hw->status & ROSC_STATUS_BADWRITE_BITS);
}
inline static void rosc_write(io_rw_32 *addr, uint32_t value) {
rosc_clear_bad_write();
assert(rosc_write_okay());
*addr = value;
assert(rosc_write_okay());
};
#ifdef __cplusplus
}
#endif
#endif
+61
View File
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico.h"
// For MHZ definitions etc
#include "hardware/clocks.h"
#include "hardware/rosc.h"
// Given a ROSC delay stage code, return the next-numerically-higher code.
// Top result bit is set when called on maximum ROSC code.
uint32_t next_rosc_code(uint32_t code) {
return ((code | 0x08888888u) + 1u) & 0xf7777777u;
}
uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz) {
// TODO: This could be a lot better
rosc_set_div(1);
for (uint32_t code = 0; code <= 0x77777777u; code = next_rosc_code(code)) {
rosc_set_freq(code);
uint rosc_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC) / 1000;
if ((rosc_mhz >= low_mhz) && (rosc_mhz <= high_mhz)) {
return rosc_mhz;
}
}
return 0;
}
void rosc_set_div(uint32_t div) {
assert(div <= 31 && div >= 1);
rosc_write(&rosc_hw->div, ROSC_DIV_VALUE_PASS + div);
}
void rosc_set_freq(uint32_t code) {
rosc_write(&rosc_hw->freqa, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code & 0xffffu));
rosc_write(&rosc_hw->freqb, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code >> 16u));
}
void rosc_set_range(uint range) {
// Range should use enumvals from the headers and thus have the password correct
rosc_write(&rosc_hw->ctrl, (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range);
}
void rosc_disable(void) {
uint32_t tmp = rosc_hw->ctrl;
tmp &= (~ROSC_CTRL_ENABLE_BITS);
tmp |= (ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB);
rosc_write(&rosc_hw->ctrl, tmp);
// Wait for stable to go away
while(rosc_hw->status & ROSC_STATUS_STABLE_BITS);
}
void rosc_set_dormant(void) {
// WARNING: This stops the rosc until woken up by an irq
rosc_write(&rosc_hw->dormant, ROSC_DORMANT_VALUE_DORMANT);
// Wait for it to become stable once woken up
while(!(rosc_hw->status & ROSC_STATUS_STABLE_BITS));
}