mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[ion/bootloader] Made suspend work
This commit is contained in:
@@ -345,12 +345,20 @@ void shutdownClocks(bool keepLEDAwake) {
|
||||
RCC.AHB2ENR()->set(0); // Reset value
|
||||
|
||||
// AHB3 bus
|
||||
RCC.AHB3ENR()->set(0); // Reset value
|
||||
class RCC::AHB3ENR ahb3enr(0); // Reset value
|
||||
// Required by external flash
|
||||
ahb3enr.setQSPIEN(true);
|
||||
RCC.AHB3ENR()->set(ahb3enr); // Reset value
|
||||
|
||||
// APB1
|
||||
class RCC::APB1ENR apb1enr(0); // Reset value
|
||||
// AHB1 bus
|
||||
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
|
||||
// GPIO B, C, D, E are used the by external flash
|
||||
ahb1enr.setGPIOBEN(true);
|
||||
ahb1enr.setGPIOCEN(true);
|
||||
ahb1enr.setGPIODEN(true);
|
||||
ahb1enr.setGPIOEEN(true);
|
||||
if (keepLEDAwake) {
|
||||
apb1enr.setTIM3EN(true);
|
||||
ahb1enr.setGPIOBEN(true);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <drivers/config/clocks.h>
|
||||
#include <drivers/trampoline.h>
|
||||
#include <ion/timing.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
@@ -126,7 +127,7 @@ static constexpr OperatingModes sOperatingModes111(QUADSPI::CCR::OperatingMode::
|
||||
static constexpr OperatingModes sOperatingModes114(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad);
|
||||
static constexpr OperatingModes sOperatingModes144(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad, QUADSPI::CCR::OperatingMode::Quad);
|
||||
|
||||
static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single;
|
||||
// static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single;
|
||||
|
||||
static constexpr int ClockFrequencyDivisor = 2; // F(QUADSPI) = F(AHB) / ClockFrequencyDivisor
|
||||
static constexpr int FastReadQuadIODummyCycles = 4; // Must be 4 for W25Q64JV (Fig 24.A page 34) and for AT25F641 (table 7.19 page 28)
|
||||
@@ -304,93 +305,12 @@ static void send_command_full(QUADSPI::CCR::FunctionalMode functionalMode, Opera
|
||||
}
|
||||
}
|
||||
|
||||
static void initGPIO() {
|
||||
for(const AFGPIOPin & p : Config::Pins) {
|
||||
p.init();
|
||||
}
|
||||
}
|
||||
|
||||
static void initQSPI() {
|
||||
// Enable QUADSPI AHB3 peripheral clock
|
||||
RCC.AHB3ENR()->setQSPIEN(true);
|
||||
|
||||
// Configure controller for target device
|
||||
class QUADSPI::DCR dcr(0);
|
||||
dcr.setFSIZE(NumberOfAddressBitsInChip - 1);
|
||||
constexpr int ChipSelectHighTimeCycles = (ChipSelectHighTimeInNanoSeconds * static_cast<float>(Clocks::Config::AHBFrequency)) / (static_cast<float>(ClockFrequencyDivisor) * 1000.0f) + 1.0f;
|
||||
dcr.setCSHT(ChipSelectHighTimeCycles - 1);
|
||||
dcr.setCKMODE(true);
|
||||
QUADSPI.DCR()->set(dcr);
|
||||
class QUADSPI::CR cr(0);
|
||||
cr.setPRESCALER(ClockFrequencyDivisor - 1);
|
||||
cr.setEN(true);
|
||||
QUADSPI.CR()->set(cr);
|
||||
}
|
||||
|
||||
static void initChip() {
|
||||
// Release sleep deep
|
||||
send_command(Command::ReleaseDeepPowerDown);
|
||||
Timing::usleep(3);
|
||||
|
||||
/* The chip initially expects commands in SPI mode. We need to use SPI to tell
|
||||
* it to switch to QuadSPI/QPI. */
|
||||
if (sOperatingMode == QUADSPI::CCR::OperatingMode::Single) {
|
||||
send_command(Command::WriteEnable);
|
||||
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
|
||||
statusRegister2.setQE(true);
|
||||
wait();
|
||||
send_write_command(Command::WriteStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&statusRegister2), sizeof(statusRegister2), sOperatingModes101);
|
||||
wait();
|
||||
sOperatingMode = QUADSPI::CCR::OperatingMode::Quad;
|
||||
}
|
||||
set_as_memory_mapped();
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (Config::NumberOfSectors == 0) {
|
||||
return;
|
||||
}
|
||||
initGPIO();
|
||||
initQSPI();
|
||||
initChip();
|
||||
}
|
||||
|
||||
static void shutdownGPIO() {
|
||||
for(const AFGPIOPin & p : Config::Pins) {
|
||||
p.group().OSPEEDR()->setOutputSpeed(p.pin(), GPIO::OSPEEDR::OutputSpeed::Low);
|
||||
p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Analog);
|
||||
p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None);
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdownChip() {
|
||||
unset_memory_mapped_mode();
|
||||
// Reset
|
||||
send_command(Command::EnableReset);
|
||||
send_command(Command::Reset);
|
||||
sOperatingMode = QUADSPI::CCR::OperatingMode::Single;
|
||||
Timing::usleep(30);
|
||||
|
||||
// Sleep deep
|
||||
send_command(Command::DeepPowerDown);
|
||||
Timing::usleep(3);
|
||||
}
|
||||
|
||||
static void shutdownQSPI() {
|
||||
// Reset the controller
|
||||
RCC.AHB3RSTR()->setQSPIRST(true);
|
||||
RCC.AHB3RSTR()->setQSPIRST(false);
|
||||
|
||||
RCC.AHB3ENR()->setQSPIEN(false); // TODO: move in Device::shutdownClocks
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (Config::NumberOfSectors == 0) {
|
||||
return;
|
||||
}
|
||||
shutdownChip();
|
||||
shutdownQSPI();
|
||||
shutdownGPIO();
|
||||
assert(false);
|
||||
}
|
||||
|
||||
int SectorAtAddress(uint32_t address) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <drivers/board.h>
|
||||
#include <drivers/power.h>
|
||||
#include <drivers/keyboard.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <drivers/trampoline.h>
|
||||
#include <drivers/wakeup.h>
|
||||
#include <regs/regs.h>
|
||||
|
||||
@@ -56,6 +58,30 @@ void standbyConfiguration() {
|
||||
CORTEX.SCR()->setSLEEPDEEP(true); // Allow Cortex-M7 deepsleep state
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) {
|
||||
// Shutdown all clocks (except the ones used by LED if active and the one used by the flash)
|
||||
Device::Board::shutdownClocks(isLEDActive);
|
||||
|
||||
Device::Power::enterLowPowerMode();
|
||||
|
||||
/* A hardware event triggered a wake up, we determine if the device should
|
||||
* wake up. We wake up when:
|
||||
* - only the power key was down
|
||||
* - the unplugged device was plugged
|
||||
* - the battery stopped charging */
|
||||
Device::Board::initClocks();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashStandby() {
|
||||
Device::Board::shutdownClocks();
|
||||
Device::Power::enterLowPowerMode();
|
||||
Device::Reset::coreWhilePlugged();
|
||||
}
|
||||
|
||||
void enterLowPowerMode() {
|
||||
reinterpret_cast<void(*)(void)>(Ion::Device::Trampoline::address(Ion::Device::Trampoline::Suspend))();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <drivers/power.h>
|
||||
#include <drivers/board.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <drivers/wakeup.h>
|
||||
|
||||
namespace Ion {
|
||||
@@ -29,6 +30,38 @@ void configWakeUp() {
|
||||
Device::WakeUp::onUSBPlugging();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) {
|
||||
// Shutdown all clocks (except the ones used by LED if active)
|
||||
Device::Board::shutdownClocks(isLEDActive);
|
||||
|
||||
Device::Power::enterLowPowerMode();
|
||||
|
||||
/* A hardware event triggered a wake up, we determine if the device should
|
||||
* wake up. We wake up when:
|
||||
* - only the power key was down
|
||||
* - the unplugged device was plugged
|
||||
* - the battery stopped charging */
|
||||
Device::Board::initClocks();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashStandby() {
|
||||
Device::Board::shutdownClocks();
|
||||
Device::Power::enterLowPowerMode();
|
||||
Device::Reset::coreWhilePlugged();
|
||||
}
|
||||
|
||||
void enterLowPowerMode() {
|
||||
/* To enter sleep, we need to issue a WFE instruction, which waits for the
|
||||
* event flag to be set and then clears it. However, the event flag might
|
||||
* already be on. So the safest way to make sure we actually wait for a new
|
||||
* event is to force the event flag to on (SEV instruction), use a first WFE
|
||||
* to clear it, and then a second WFE to wait for a _new_ event. */
|
||||
asm("sev");
|
||||
asm("wfe");
|
||||
asm("nop");
|
||||
asm("wfe");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <drivers/board.h>
|
||||
#include <drivers/external_flash.h>
|
||||
#include <drivers/power.h>
|
||||
#include <drivers/keyboard.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <drivers/wakeup.h>
|
||||
#include <regs/regs.h>
|
||||
|
||||
@@ -56,6 +58,43 @@ void standbyConfiguration() {
|
||||
CORTEX.SCR()->setSLEEPDEEP(true); // Allow Cortex-M7 deepsleep state
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) {
|
||||
// Shutdown the external flash
|
||||
Device::ExternalFlash::shutdown();
|
||||
// Shutdown all clocks (except the ones used by LED if active)
|
||||
Device::Board::shutdownClocks(isLEDActive);
|
||||
|
||||
Device::Power::enterLowPowerMode();
|
||||
|
||||
/* A hardware event triggered a wake up, we determine if the device should
|
||||
* wake up. We wake up when:
|
||||
* - only the power key was down
|
||||
* - the unplugged device was plugged
|
||||
* - the battery stopped charging */
|
||||
Device::Board::initClocks();
|
||||
// Init external flash
|
||||
Device::ExternalFlash::init();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashStandby() {
|
||||
Device::ExternalFlash::shutdown();
|
||||
Device::Board::shutdownClocks();
|
||||
Device::Power::enterLowPowerMode();
|
||||
Device::Reset::coreWhilePlugged();
|
||||
}
|
||||
|
||||
void enterLowPowerMode() {
|
||||
/* To enter sleep, we need to issue a WFE instruction, which waits for the
|
||||
* event flag to be set and then clears it. However, the event flag might
|
||||
* already be on. So the safest way to make sure we actually wait for a new
|
||||
* event is to force the event flag to on (SEV instruction), use a first WFE
|
||||
* to clear it, and then a second WFE to wait for a _new_ event. */
|
||||
asm("sev");
|
||||
asm("wfe");
|
||||
asm("nop");
|
||||
asm("wfe");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,31 +100,6 @@ namespace Power {
|
||||
// Public Power methods
|
||||
using namespace Device::Regs;
|
||||
|
||||
void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) {
|
||||
// Shutdown the external flash
|
||||
Device::ExternalFlash::shutdown();
|
||||
// Shutdown all clocks (except the ones used by LED if active)
|
||||
Device::Board::shutdownClocks(isLEDActive);
|
||||
|
||||
Device::Power::enterLowPowerMode();
|
||||
|
||||
/* A hardware event triggered a wake up, we determine if the device should
|
||||
* wake up. We wake up when:
|
||||
* - only the power key was down
|
||||
* - the unplugged device was plugged
|
||||
* - the battery stopped charging */
|
||||
Device::Board::initClocks();
|
||||
// Init external flash
|
||||
Device::ExternalFlash::init();
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) internalFlashStandby() {
|
||||
Device::ExternalFlash::shutdown();
|
||||
Device::Board::shutdownClocks();
|
||||
Device::Power::enterLowPowerMode();
|
||||
Device::Reset::coreWhilePlugged();
|
||||
}
|
||||
|
||||
void stopConfiguration() {
|
||||
PWR.CR()->setMRUDS(true); // Main regulator in Low Voltage and Flash memory in Deep Sleep mode when the device is in Stop mode
|
||||
PWR.CR()->setLPUDS(true); // Low-power regulator in under-drive mode if LPDS bit is set and Flash memory in power-down when the device is in Stop under-drive mode
|
||||
@@ -163,18 +138,6 @@ void waitUntilOnOffKeyReleased() {
|
||||
Timing::msleep(100);
|
||||
}
|
||||
|
||||
void enterLowPowerMode() {
|
||||
/* To enter sleep, we need to issue a WFE instruction, which waits for the
|
||||
* event flag to be set and then clears it. However, the event flag might
|
||||
* already be on. So the safest way to make sure we actually wait for a new
|
||||
* event is to force the event flag to on (SEV instruction), use a first WFE
|
||||
* to clear it, and then a second WFE to wait for a _new_ event. */
|
||||
asm("sev");
|
||||
asm("wfe");
|
||||
asm("nop");
|
||||
asm("wfe");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user