From 5e130cc4f28f7ee481862b1762bdacb067c36529 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 22 Feb 2022 23:47:53 +0300 Subject: [PATCH] [ion/bootloader] Made suspend work --- ion/src/device/bootloader/drivers/board.cpp | 10 ++- .../drivers/external_flash_tramp.cpp | 88 +------------------ ion/src/device/bootloader/drivers/power.cpp | 26 ++++++ ion/src/device/n0100/drivers/power.cpp | 33 +++++++ ion/src/device/n0110/drivers/power.cpp | 39 ++++++++ ion/src/device/shared/drivers/power.cpp | 37 -------- 6 files changed, 111 insertions(+), 122 deletions(-) diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/bootloader/drivers/board.cpp index 1d61eef86..3db56ee2a 100644 --- a/ion/src/device/bootloader/drivers/board.cpp +++ b/ion/src/device/bootloader/drivers/board.cpp @@ -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); diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp index f42cdbd5b..031722356 100644 --- a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp +++ b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp @@ -4,6 +4,7 @@ #include #include #include +#include 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(Clocks::Config::AHBFrequency)) / (static_cast(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(FlashAddressSpaceSize), reinterpret_cast(&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) { diff --git a/ion/src/device/bootloader/drivers/power.cpp b/ion/src/device/bootloader/drivers/power.cpp index f8f657dea..f38f9e978 100644 --- a/ion/src/device/bootloader/drivers/power.cpp +++ b/ion/src/device/bootloader/drivers/power.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include @@ -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(Ion::Device::Trampoline::address(Ion::Device::Trampoline::Suspend))(); +} + } } } diff --git a/ion/src/device/n0100/drivers/power.cpp b/ion/src/device/n0100/drivers/power.cpp index 4f7a5b8fd..e14012b71 100644 --- a/ion/src/device/n0100/drivers/power.cpp +++ b/ion/src/device/n0100/drivers/power.cpp @@ -1,5 +1,6 @@ #include #include +#include #include 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"); +} + } } } diff --git a/ion/src/device/n0110/drivers/power.cpp b/ion/src/device/n0110/drivers/power.cpp index f8f657dea..1286bc883 100644 --- a/ion/src/device/n0110/drivers/power.cpp +++ b/ion/src/device/n0110/drivers/power.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include @@ -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"); +} + } } } diff --git a/ion/src/device/shared/drivers/power.cpp b/ion/src/device/shared/drivers/power.cpp index 0e5b35267..bba6444c3 100644 --- a/ion/src/device/shared/drivers/power.cpp +++ b/ion/src/device/shared/drivers/power.cpp @@ -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"); -} - } } }