271 lines
7.1 KiB
C++
271 lines
7.1 KiB
C++
/**
|
|
* @file stm32_common.cpp
|
|
* @brief Low level common STM32 code
|
|
*
|
|
* @date Mar 28, 2019
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
|
*/
|
|
|
|
#include "pch.h"
|
|
|
|
#include <rusefi/expected.h>
|
|
#include "hardware.h"
|
|
|
|
#ifdef STM32F4XX
|
|
#include "stm32f4xx_hal_flash.h"
|
|
#elif defined(STM32F7XX)
|
|
#include "stm32f7xx_hal_flash.h"
|
|
#elif defined(STM32H7XX)
|
|
#include "stm32h7xx_hal_flash.h"
|
|
#endif
|
|
|
|
#if EFI_USE_OPENBLT
|
|
/* communication with OpenBLT that is plain C, not to modify external file */
|
|
extern "C" {
|
|
#include "openblt/shared_params.h"
|
|
};
|
|
#endif
|
|
|
|
#if EFI_PROD_CODE
|
|
#include "mpu_util.h"
|
|
#include "backup_ram.h"
|
|
#endif /* EFI_PROD_CODE */
|
|
|
|
#if EFI_PROD_CODE
|
|
|
|
static void reset_and_jump(void) {
|
|
#ifdef STM32H7XX
|
|
// H7 needs a forcible reset of the USB peripheral(s) in order for the bootloader to work properly.
|
|
// If you don't do this, the bootloader will execute, but USB doesn't work (nobody knows why)
|
|
// See https://community.st.com/s/question/0D53W00000vQEWsSAO/stm32h743-dfu-entry-doesnt-work-unless-boot0-held-high-at-poweron
|
|
RCC->AHB1ENR &= ~(RCC_AHB1ENR_USB1OTGHSEN | RCC_AHB1ENR_USB2OTGFSEN);
|
|
#endif
|
|
|
|
// and now reboot
|
|
NVIC_SystemReset();
|
|
}
|
|
|
|
#if EFI_DFU_JUMP
|
|
void jump_to_bootloader() {
|
|
// leave DFU breadcrumb which assembly startup code would check, see [rusefi][DFU] section in assembly code
|
|
|
|
*((unsigned long *)0x2001FFF0) = 0xDEADBEEF; // End of RAM
|
|
|
|
reset_and_jump();
|
|
}
|
|
#endif
|
|
|
|
void jump_to_openblt() {
|
|
#if EFI_USE_OPENBLT
|
|
/* safe to call on already inited shares area */
|
|
SharedParamsInit();
|
|
/* Store sing to stay in OpenBLT */
|
|
SharedParamsWriteByIndex(0, 0x01);
|
|
|
|
reset_and_jump();
|
|
#endif
|
|
}
|
|
#endif /* EFI_PROD_CODE */
|
|
|
|
#if EFI_PROD_CODE
|
|
|
|
BOR_Level_t BOR_Get(void) {
|
|
FLASH_OBProgramInitTypeDef FLASH_Handle;
|
|
|
|
/* Read option bytes */
|
|
HAL_FLASHEx_OBGetConfig(&FLASH_Handle);
|
|
|
|
/* Return BOR value */
|
|
return (BOR_Level_t) FLASH_Handle.BORLevel;
|
|
}
|
|
|
|
BOR_Result_t BOR_Set(BOR_Level_t BORValue) {
|
|
if (BOR_Get() == BORValue) {
|
|
return BOR_Result_Ok;
|
|
}
|
|
|
|
|
|
FLASH_OBProgramInitTypeDef FLASH_Handle;
|
|
|
|
FLASH_Handle.BORLevel = (uint32_t)BORValue;
|
|
FLASH_Handle.OptionType = OPTIONBYTE_BOR;
|
|
|
|
HAL_FLASH_OB_Unlock();
|
|
|
|
HAL_FLASHEx_OBProgram(&FLASH_Handle);
|
|
|
|
HAL_StatusTypeDef status = HAL_FLASH_OB_Launch();
|
|
|
|
HAL_FLASH_OB_Lock();
|
|
|
|
if (status != HAL_OK) {
|
|
return BOR_Result_Error;
|
|
}
|
|
|
|
return BOR_Result_Ok;
|
|
}
|
|
|
|
void startWatchdog(int timeoutMs) {
|
|
#if HAL_USE_WDG
|
|
// RL is a 12-bit value so we use a "2 ms" prescaler to support long timeouts (> 4.095 sec)
|
|
static WDGConfig wdgcfg;
|
|
wdgcfg.pr = STM32_IWDG_PR_64; // t = (1/32768) * 64 = ~2 ms
|
|
wdgcfg.rlr = STM32_IWDG_RL((uint32_t)((32.768f / 64.0f) * timeoutMs));
|
|
#if 0
|
|
efiPrintf("[wdgStart]");
|
|
#endif
|
|
wdgStart(&WDGD1, &wdgcfg);
|
|
#endif // HAL_USE_WDG
|
|
}
|
|
|
|
static efitimems_t watchdogResetPeriodMs = 0;
|
|
|
|
void setWatchdogResetPeriod(int resetMs) {
|
|
#if 0
|
|
efiPrintf("[dev] wd %d", resetMs);
|
|
#endif
|
|
watchdogResetPeriodMs = (efitimems_t)resetMs;
|
|
}
|
|
|
|
void tryResetWatchdog() {
|
|
#if HAL_USE_WDG
|
|
static Timer lastTimeWasReset;
|
|
// check if it's time to reset the watchdog
|
|
if (lastTimeWasReset.hasElapsedMs(watchdogResetPeriodMs)) {
|
|
// we assume tryResetWatchdog() is called from a timer callback
|
|
wdgResetI(&WDGD1);
|
|
lastTimeWasReset.reset();
|
|
}
|
|
#endif // HAL_USE_WDG
|
|
}
|
|
|
|
void baseMCUInit(void) {
|
|
// looks like this holds a random value on start? Let's set a nice clean zero
|
|
DWT->CYCCNT = 0;
|
|
|
|
BOR_Set(BOR_Level_1); // one step above default value
|
|
|
|
setWatchdogResetPeriod(WATCHDOG_RESET_MS);
|
|
startWatchdog();
|
|
}
|
|
|
|
extern uint32_t __main_stack_base__;
|
|
|
|
typedef struct port_intctx intctx_t;
|
|
|
|
EXTERNC int getRemainingStack(thread_t *otp) {
|
|
#if CH_DBG_ENABLE_STACK_CHECK
|
|
// this would dismiss coverity warning - see http://rusefi.com/forum/viewtopic.php?f=5&t=655
|
|
// coverity[uninit_use]
|
|
register intctx_t *r13 asm ("r13");
|
|
otp->activeStack = r13;
|
|
|
|
int remainingStack;
|
|
if (ch.dbg.isr_cnt > 0) {
|
|
// ISR context
|
|
remainingStack = (int)(r13 - 1) - (int)&__main_stack_base__;
|
|
} else {
|
|
remainingStack = (int)(r13 - 1) - (int)otp->wabase;
|
|
}
|
|
otp->remainingStack = remainingStack;
|
|
return remainingStack;
|
|
#else
|
|
UNUSED(otp);
|
|
return 99999;
|
|
#endif /* CH_DBG_ENABLE_STACK_CHECK */
|
|
}
|
|
|
|
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
|
|
|
|
#define HWREG(x) \
|
|
(*((volatile unsigned long *)(x)))
|
|
|
|
#define NVIC_FAULT_STAT 0xE000ED28 // Configurable Fault Status
|
|
#define NVIC_FAULT_STAT_BFARV 0x00008000 // Bus Fault Address Register Valid
|
|
#define NVIC_CFG_CTRL_BFHFNMIGN 0x00000100 // Ignore Bus Fault in NMI and
|
|
// Fault
|
|
#define NVIC_CFG_CTRL 0xE000ED14 // Configuration and Control
|
|
|
|
|
|
/**
|
|
* @brief Probe an address to see if can be read without generating a bus fault
|
|
* @details This function must be called with the processor in privileged mode.
|
|
* It:
|
|
* - Clear any previous indication of a bus fault in the BFARV bit
|
|
* - Temporarily sets the processor to Ignore Bus Faults with all interrupts and fault handlers disabled
|
|
* - Attempt to read from read_address, ignoring the result
|
|
* - Checks to see if the read caused a bus fault, by checking the BFARV bit is set
|
|
* - Re-enables Bus Faults and all interrupts and fault handlers
|
|
* @param[in] read_address The address to try reading a byte from
|
|
* @return Returns true if no bus fault occurred reading from read_address, or false if a bus fault occurred.
|
|
*/
|
|
bool ramReadProbe(volatile const char *read_address) {
|
|
bool address_readable = true;
|
|
|
|
/* Clear any existing indication of a bus fault - BFARV is write one to clear */
|
|
HWREG (NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
|
|
|
|
HWREG (NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
|
|
asm volatile (" CPSID f;");
|
|
*read_address;
|
|
if ((HWREG (NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
|
|
{
|
|
address_readable = false;
|
|
}
|
|
asm volatile (" CPSIE f;");
|
|
HWREG (NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
|
|
|
|
return address_readable;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(STM32F4)
|
|
bool isStm32F42x() {
|
|
// really it's enough to just check 0x20020010
|
|
return ramReadProbe((const char *)0x20000010) && ramReadProbe((const char *)0x20020010) && !ramReadProbe((const char *)0x20070010);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
// Stubs for per-board low power helpers
|
|
PUBLIC_API_WEAK void boardPrepareForStop() {
|
|
// Default implementation - wake up on PA0 - boards should override this
|
|
palEnableLineEvent(PAL_LINE(GPIOA, 0), PAL_EVENT_MODE_RISING_EDGE);
|
|
}
|
|
|
|
/**
|
|
Standby uses special low power hardware - it always wakes on rising edge
|
|
*/
|
|
|
|
void boardPreparePA0ForStandby() {
|
|
#ifdef STM32F4XX
|
|
//Enable Wakeup Pin for PA0
|
|
PWR->CSR |= PWR_CSR_EWUP;
|
|
|
|
// Clear wakeup flag - it may be set if PA0 is already
|
|
// high when we enable it as a wake source
|
|
PWR->CR |= PWR_CR_CWUF; //Clear Wakeup Pin flag for PA0
|
|
#endif
|
|
|
|
#ifdef STM32F7XX
|
|
PWR->CSR2 |= PWR_CSR2_EWUP1; //EWUP1: Enable Wakeup pin for PA0
|
|
PWR->CR2 |= PWR_CR2_CWUPF1; //Clear Wakeup Pin flag for PA0
|
|
#endif
|
|
|
|
#ifdef STM32H7XX
|
|
// Wake on wakeup pin 0 - PA0
|
|
PWR->WKUPEPR = PWR_WKUPEPR_WKUPEN1;
|
|
|
|
// clear all possible wakeup bits
|
|
PWR->WKUPCR = 0xFFFFFFFF;
|
|
#endif
|
|
}
|
|
|
|
PUBLIC_API_WEAK void boardPrepareForStandby() {
|
|
boardPreparePA0ForStandby();
|
|
}
|
|
|
|
#endif // EFI_PROD_CODE
|