blob: 51948326e23ade52755b587bd159a0e9a6b6194d [file] [log] [blame]
#include <stdint.h>
#include <STM32F2XX.h>
#include "cape/bootloader_handoff.h"
#include "cape/led.h"
#include "cape/util.h"
// Actually runs the bootloader code.
// Implemented in bootloader_impl.c.
void bootloader_start(void) __attribute__((noreturn));
// Sets everything up and then jumps to the main code.
static void jump_to_main(void) __attribute__((noreturn));
static void jump_to_main(void) {
__asm__ __volatile__(
"mov sp, %[stack]\n\t"
"bx %[reset]" : :
[stack]"r"(RAM_START + RAM_SIZE), [reset]"r"(MAIN_FLASH_START | 1)
: "memory");
__builtin_unreachable();
}
static void setup_main_clock(void) {
// We set up a couple of registers here separately from the actual register to
// avoid having to think about what happens when the value is in some
// intermediate state.
RCC->CR |= RCC_CR_HSEON; // get the HSE oscillator going
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN |
FLASH_ACR_LATENCY_3WS;
uint32_t rcc_pllcfgr = 0;
rcc_pllcfgr |= RCC_PLLCFGR_PLLSRC; // use the external oscillator
// 8MHz * 120 / 4 / 2 = 120MHz
rcc_pllcfgr |= 120 << 6; // multiplier
rcc_pllcfgr |= 4; // divider
rcc_pllcfgr |= 5 << 24; // other stuff divider = 5, just in case
RCC->PLLCFGR = rcc_pllcfgr;
uint32_t rcc_cfgr = 0;
rcc_cfgr |= 4 << 13; // APB2 divider = 2
rcc_cfgr |= 5 << 10; // APB1 divider = 4
rcc_cfgr |= 2; // use the PLL
// Wait for the HSE oscillator to be stable.
while (!(RCC->CR & RCC_CR_HSERDY)) {}
RCC->CR |= RCC_CR_PLLON;
// Wait for the main PLL to be stable.
while (!(RCC->CR & RCC_CR_PLLRDY)) {}
// Wait until the flash is using 3 wait states.
while ((FLASH->ACR & 7) != 3) {}
RCC->CFGR = rcc_cfgr;
// Wait until we are using the PLL as our main clock source.
while ((RCC->CFGR & (3 << 2)) != (2 << 2)) {}
}
void _start(void) {
// Enable the GPIO pin clocks.
// We don't have anything attached to the 1 port D pin, so don't bother
// enabling it.
RCC->AHB1ENR |=
RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
led_init();
led_set(LED_HB, 1);
gpio_set_pupd(GPIOC, 2, 2);
setup_main_clock();
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
SYSCFG->CMPCR |= SYSCFG_CMPCR_CMP_PD; // enable IO compensation cell
while (!(SYSCFG->CMPCR & SYSCFG_CMPCR_READY)) {} // wait for it to be ready
if (GPIOC->IDR & (1 << 2)) {
jump_to_main();
} else {
bootloader_start();
}
}