[ion/bootloader] Made suspend work

This commit is contained in:
M4x1m3
2022-02-22 23:47:53 +03:00
parent 2b5a773993
commit 5e130cc4f2
6 changed files with 111 additions and 122 deletions

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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))();
}
}
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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");
}
}
}
}