529 lines
14 KiB
C
529 lines
14 KiB
C
#include <alloca.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include <variant/variant.h>
|
|
|
|
#include <plat/pwr.h>
|
|
#include <plat/gpio.h>
|
|
#include <plat/cmsis.h>
|
|
|
|
#include <bl.h>
|
|
#include <gpio.h>
|
|
|
|
struct StmUdid
|
|
{
|
|
volatile uint32_t U_ID[3];
|
|
};
|
|
|
|
struct StmSpi {
|
|
volatile uint32_t CR1;
|
|
volatile uint32_t CR2;
|
|
volatile uint32_t SR;
|
|
volatile uint32_t DR;
|
|
volatile uint32_t CRCPR;
|
|
volatile uint32_t RXCRCR;
|
|
volatile uint32_t TXCRCR;
|
|
volatile uint32_t I2SCFGR;
|
|
volatile uint32_t I2SPR;
|
|
};
|
|
|
|
struct StmGpio {
|
|
volatile uint32_t MODER;
|
|
volatile uint32_t OTYPER;
|
|
volatile uint32_t OSPEEDR;
|
|
volatile uint32_t PUPDR;
|
|
volatile uint32_t IDR;
|
|
volatile uint32_t ODR;
|
|
volatile uint32_t BSRR;
|
|
volatile uint32_t LCKR;
|
|
volatile uint32_t AFR[2];
|
|
};
|
|
|
|
struct StmFlash
|
|
{
|
|
volatile uint32_t ACR;
|
|
volatile uint32_t KEYR;
|
|
volatile uint32_t OPTKEYR;
|
|
volatile uint32_t SR;
|
|
volatile uint32_t CR;
|
|
volatile uint32_t OPTCR;
|
|
};
|
|
|
|
struct StmCrc
|
|
{
|
|
volatile uint32_t DR;
|
|
volatile uint32_t IDR;
|
|
volatile uint32_t CR;
|
|
};
|
|
|
|
struct StmRcc {
|
|
volatile uint32_t CR;
|
|
volatile uint32_t PLLCFGR;
|
|
volatile uint32_t CFGR;
|
|
volatile uint32_t CIR;
|
|
volatile uint32_t AHB1RSTR;
|
|
volatile uint32_t AHB2RSTR;
|
|
volatile uint32_t AHB3RSTR;
|
|
uint8_t unused0[4];
|
|
volatile uint32_t APB1RSTR;
|
|
volatile uint32_t APB2RSTR;
|
|
uint8_t unused1[8];
|
|
volatile uint32_t AHB1ENR;
|
|
volatile uint32_t AHB2ENR;
|
|
volatile uint32_t AHB3ENR;
|
|
uint8_t unused2[4];
|
|
volatile uint32_t APB1ENR;
|
|
volatile uint32_t APB2ENR;
|
|
uint8_t unused3[8];
|
|
volatile uint32_t AHB1LPENR;
|
|
volatile uint32_t AHB2LPENR;
|
|
volatile uint32_t AHB3LPENR;
|
|
uint8_t unused4[4];
|
|
volatile uint32_t APB1LPENR;
|
|
volatile uint32_t APB2LPENR;
|
|
uint8_t unused5[8];
|
|
volatile uint32_t BDCR;
|
|
volatile uint32_t CSR;
|
|
uint8_t unused6[8];
|
|
volatile uint32_t SSCGR;
|
|
volatile uint32_t PLLI2SCFGR;
|
|
};
|
|
|
|
typedef void (*FlashEraseF)(volatile uint32_t *, uint32_t, volatile uint32_t *);
|
|
typedef void (*FlashWriteF)(volatile uint8_t *, uint8_t, volatile uint32_t *);
|
|
|
|
static struct StmSpi *SPI;
|
|
static struct StmRcc *RCC;
|
|
static struct Gpio *wakeupGpio;
|
|
static uint32_t mOldApb2State;
|
|
static uint32_t mOldAhb1State;
|
|
|
|
#define FLASH_ACR_LAT(x) ((x) & FLASH_ACR_LAT_MASK)
|
|
#define FLASH_ACR_LAT_MASK 0x0F
|
|
#define FLASH_ACR_PRFTEN 0x00000100
|
|
#define FLASH_ACR_ICEN 0x00000200
|
|
#define FLASH_ACR_DCEN 0x00000400
|
|
#define FLASH_ACR_ICRST 0x00000800
|
|
#define FLASH_ACR_DCRST 0x00001000
|
|
|
|
#define FLASH_SR_EOP 0x00000001
|
|
#define FLASH_SR_OPERR 0x00000002
|
|
#define FLASH_SR_WRPERR 0x00000010
|
|
#define FLASH_SR_PGAERR 0x00000020
|
|
#define FLASH_SR_PGPERR 0x00000040
|
|
#define FLASH_SR_PGSERR 0x00000080
|
|
#define FLASH_SR_RDERR 0x00000100
|
|
#define FLASH_SR_BSY 0x00010000
|
|
|
|
#define FLASH_CR_PG 0x00000001
|
|
#define FLASH_CR_SER 0x00000002
|
|
#define FLASH_CR_MER 0x00000004
|
|
#define FLASH_CR_SNB(x) (((x) << FLASH_CR_SNB_SHIFT) & FLASH_CR_SNB_MASK)
|
|
#define FLASH_CR_SNB_MASK 0x00000078
|
|
#define FLASH_CR_SNB_SHIFT 3
|
|
#define FLASH_CR_PSIZE(x) (((x) << FLASH_CR_PSIZE_SHIFT) & FLASH_CR_PSIZE_MASK)
|
|
#define FLASH_CR_PSIZE_MASK 0x00000300
|
|
#define FLASH_CR_PSIZE_SHIFT 8
|
|
#define FLASH_CR_PSIZE_8 0x0
|
|
#define FLASH_CR_PSIZE_16 0x1
|
|
#define FLASH_CR_PSIZE_32 0x2
|
|
#define FLASH_CR_PSIZE_64 0x3
|
|
#define FLASH_CR_STRT 0x00010000
|
|
#define FLASH_CR_EOPIE 0x01000000
|
|
#define FLASH_CR_ERRIE 0x02000000
|
|
#define FLASH_CR_LOCK 0x80000000
|
|
|
|
//stm defines
|
|
#define BL_MAX_FLASH_CODE 1024
|
|
|
|
/*
|
|
* Return the address of the erase code and the length of the code
|
|
*
|
|
* This code needs to run out of ram and not flash since accessing flash
|
|
* while erasing is undefined (best case the processor stalls, worst case
|
|
* it starts executing garbage)
|
|
*
|
|
* This function is used to get a pointer to the actual code that does the
|
|
* erase and polls for completion (so we can copy it to ram) as well as the
|
|
* length of the code (so we know how much space to allocate for it)
|
|
*
|
|
* void FlashEraseF(volatile uint32_t *addr, uint32_t value, volatile uint32_t *status)
|
|
* {
|
|
* *addr = value;
|
|
* while (*status & FLASH_SR_BSY) ;
|
|
* }
|
|
*/
|
|
static void __attribute__((naked)) blGetFlashEraseCode(uint16_t **addr, uint32_t *size)
|
|
{
|
|
asm volatile (
|
|
" push {lr} \n"
|
|
" bl 9f \n"
|
|
" str r1, [r0, #0] \n" // *addr = value
|
|
"1: \n"
|
|
" ldr r3, [r2, #0] \n" // r3 = *status
|
|
" lsls r3, #15 \n" // r3 <<= 15
|
|
" bmi 1b \n" // if (r3 < 0) goto 1
|
|
" bx lr \n" // return
|
|
"9: \n"
|
|
" bic lr, #0x1 \n"
|
|
" adr r3, 9b \n"
|
|
" sub r3, lr \n"
|
|
" str lr, [r0] \n"
|
|
" str r3, [r1] \n"
|
|
" pop {pc} \n"
|
|
);
|
|
}
|
|
|
|
static void _blEraseSectors(uint32_t sector_cnt, uint8_t *erase_mask)
|
|
{
|
|
struct StmFlash *flash = (struct StmFlash *)FLASH_BASE;
|
|
uint16_t *code_src, *code;
|
|
uint32_t i, code_length;
|
|
FlashEraseF func;
|
|
|
|
blGetFlashEraseCode(&code_src, &code_length);
|
|
|
|
if (code_length < BL_MAX_FLASH_CODE) {
|
|
code = (uint16_t *)(((uint32_t)alloca(code_length + 1) + 1) & ~0x1);
|
|
func = (FlashEraseF)((uint8_t *)code+1);
|
|
|
|
for (i = 0; i < code_length / sizeof(uint16_t); i++)
|
|
code[i] = code_src[i];
|
|
|
|
for (i = 0; i < sector_cnt; i++) {
|
|
if (erase_mask[i]) {
|
|
flash->CR = (flash->CR & ~(FLASH_CR_SNB_MASK)) |
|
|
FLASH_CR_SNB(i) | FLASH_CR_SER;
|
|
func(&flash->CR, flash->CR | FLASH_CR_STRT, &flash->SR);
|
|
flash->CR &= ~(FLASH_CR_SNB_MASK | FLASH_CR_SER);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool blEraseSectors(uint32_t sector_cnt, uint8_t *erase_mask, uint32_t key1, uint32_t key2)
|
|
{
|
|
struct StmFlash *flash = (struct StmFlash *)FLASH_BASE;
|
|
uint32_t acr_cache, cr_cache;
|
|
// disable interrupts
|
|
// otherwise an interrupt during flash write/erase will stall the processor
|
|
// until the write/erase completes
|
|
uint32_t int_state = blDisableInts();
|
|
|
|
// wait for flash to not be busy (should never be set at this point)
|
|
while (flash->SR & FLASH_SR_BSY);
|
|
|
|
cr_cache = flash->CR;
|
|
|
|
if (flash->CR & FLASH_CR_LOCK) {
|
|
// unlock flash
|
|
flash->KEYR = key1;
|
|
flash->KEYR = key2;
|
|
}
|
|
|
|
if (!(flash->CR & FLASH_CR_LOCK)) {
|
|
flash->CR = FLASH_CR_PSIZE(FLASH_CR_PSIZE_8);
|
|
acr_cache = flash->ACR;
|
|
|
|
// disable and flush data and instruction caches
|
|
flash->ACR &= ~(FLASH_ACR_DCEN | FLASH_ACR_ICEN);
|
|
flash->ACR |= (FLASH_ACR_DCRST | FLASH_ACR_ICRST);
|
|
|
|
_blEraseSectors(sector_cnt, erase_mask);
|
|
|
|
flash->ACR = acr_cache;
|
|
flash->CR = cr_cache;
|
|
|
|
// restore interrupts
|
|
blRestoreInts(int_state);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Return the address of the write code and the length of the code
|
|
*
|
|
* This code needs to run out of ram and not flash since accessing flash
|
|
* while writing to flash is undefined (best case the processor stalls, worst
|
|
* case it starts executing garbage)
|
|
*
|
|
* This function is used to get a pointer to the actual code that does the
|
|
* write and polls for completion (so we can copy it to ram) as well as the
|
|
* length of the code (so we know how much space to allocate for it)
|
|
*
|
|
* void FlashWriteF(volatile uint8_t *addr, uint8_t value, volatile uint32_t *status)
|
|
* {
|
|
* *addr = value;
|
|
* while (*status & FLASH_SR_BSY) ;
|
|
* }
|
|
*/
|
|
static void __attribute__((naked)) blGetFlashWriteCode(uint16_t **addr, uint32_t *size)
|
|
{
|
|
asm volatile (
|
|
" push {lr} \n"
|
|
" bl 9f \n"
|
|
" strb r1, [r0, #0] \n" // *addr = value
|
|
"1: \n"
|
|
" ldr r3, [r2, #0] \n" // r3 = *status
|
|
" lsls r3, #15 \n" // r3 <<= 15
|
|
" bmi 1b \n" // if (r3 < 0) goto 1
|
|
" bx lr \n" // return
|
|
"9: \n"
|
|
" bic lr, #0x1 \n"
|
|
" adr r3, 9b \n"
|
|
" sub r3, lr \n"
|
|
" str lr, [r0] \n"
|
|
" str r3, [r1] \n"
|
|
" pop {pc} \n"
|
|
);
|
|
}
|
|
|
|
static void blWriteBytes(uint8_t *dst, const uint8_t *src, uint32_t length)
|
|
{
|
|
struct StmFlash *flash = (struct StmFlash *)FLASH_BASE;
|
|
uint16_t *code_src, *code;
|
|
uint32_t i, code_length;
|
|
FlashWriteF func;
|
|
|
|
blGetFlashWriteCode(&code_src, &code_length);
|
|
|
|
if (code_length < BL_MAX_FLASH_CODE) {
|
|
code = (uint16_t *)(((uint32_t)alloca(code_length+1) + 1) & ~0x1);
|
|
func = (FlashWriteF)((uint8_t *)code+1);
|
|
|
|
for (i = 0; i < code_length / sizeof(uint16_t); i++)
|
|
code[i] = code_src[i];
|
|
|
|
flash->CR |= FLASH_CR_PG;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
if (dst[i] != src[i])
|
|
func(&dst[i], src[i], &flash->SR);
|
|
}
|
|
|
|
flash->CR &= ~FLASH_CR_PG;
|
|
}
|
|
}
|
|
|
|
bool blPlatProgramFlash(uint8_t *dst, const uint8_t *src, uint32_t length, uint32_t key1, uint32_t key2)
|
|
{
|
|
struct StmFlash *flash = (struct StmFlash *)FLASH_BASE;
|
|
uint32_t acr_cache, cr_cache;
|
|
// disable interrupts
|
|
// otherwise an interrupt during flash write will stall the processor
|
|
// until the write completes
|
|
uint32_t int_state = blDisableInts();
|
|
|
|
// wait for flash to not be busy (should never be set at this point)
|
|
while (flash->SR & FLASH_SR_BSY);
|
|
|
|
cr_cache = flash->CR;
|
|
|
|
if (flash->CR & FLASH_CR_LOCK) {
|
|
// unlock flash
|
|
flash->KEYR = key1;
|
|
flash->KEYR = key2;
|
|
}
|
|
|
|
if (flash->CR & FLASH_CR_LOCK) {
|
|
// unlock failed, restore interrupts
|
|
blRestoreInts(int_state);
|
|
|
|
return false;
|
|
}
|
|
|
|
flash->CR = FLASH_CR_PSIZE(FLASH_CR_PSIZE_8);
|
|
|
|
acr_cache = flash->ACR;
|
|
|
|
// disable and flush data and instruction caches
|
|
flash->ACR &= ~(FLASH_ACR_DCEN | FLASH_ACR_ICEN);
|
|
flash->ACR |= (FLASH_ACR_DCRST | FLASH_ACR_ICRST);
|
|
|
|
blWriteBytes(dst, src, length);
|
|
|
|
flash->ACR = acr_cache;
|
|
flash->CR = cr_cache;
|
|
|
|
blRestoreInts(int_state);
|
|
return true;
|
|
}
|
|
|
|
uint32_t blDisableInts(void)
|
|
{
|
|
uint32_t state;
|
|
|
|
asm volatile (
|
|
"mrs %0, PRIMASK \n"
|
|
"cpsid i \n"
|
|
:"=r"(state)
|
|
);
|
|
|
|
return state;
|
|
}
|
|
|
|
void blRestoreInts(uint32_t state)
|
|
{
|
|
asm volatile(
|
|
"msr PRIMASK, %0 \n"
|
|
::"r"((uint32_t)state)
|
|
);
|
|
}
|
|
|
|
void blReboot(void)
|
|
{
|
|
SCB->AIRCR = 0x05FA0004;
|
|
//we never get here
|
|
while(1);
|
|
}
|
|
|
|
void blResetRxData()
|
|
{
|
|
(void)SPI->DR;
|
|
while (!(SPI->SR & 1));
|
|
(void)SPI->DR;
|
|
}
|
|
|
|
uint8_t blSpiTxRxByte(uint32_t val)
|
|
{
|
|
while (!(SPI->SR & 2));
|
|
SPI->DR = val;
|
|
while (!(SPI->SR & 1));
|
|
return SPI->DR;
|
|
}
|
|
|
|
uint32_t blGetSnum(uint32_t *snum, uint32_t length)
|
|
{
|
|
struct StmUdid *reg = (struct StmUdid *)UDID_BASE;
|
|
uint32_t i;
|
|
|
|
if (length > 3)
|
|
length = 3;
|
|
|
|
for (i = 0; i < length; i++)
|
|
snum[i] = reg->U_ID[i];
|
|
|
|
return (length << 2);
|
|
}
|
|
|
|
void blSetup()
|
|
{
|
|
SPI = (struct StmSpi*)SPI1_BASE;
|
|
RCC = (struct StmRcc*)RCC_BASE;
|
|
struct Gpio *gpio;
|
|
int i;
|
|
|
|
//SPI1 & GPIOA on
|
|
mOldApb2State = RCC->APB2ENR;
|
|
mOldAhb1State = RCC->AHB1ENR;
|
|
RCC->APB2ENR |= PERIPH_APB2_SPI1;
|
|
RCC->AHB1ENR |= PERIPH_AHB1_GPIOA;
|
|
|
|
//reset units
|
|
RCC->APB2RSTR |= PERIPH_APB2_SPI1;
|
|
RCC->AHB1RSTR |= PERIPH_AHB1_GPIOA;
|
|
RCC->APB2RSTR &=~ PERIPH_APB2_SPI1;
|
|
RCC->AHB1RSTR &=~ PERIPH_AHB1_GPIOA;
|
|
|
|
//configure GPIOA for SPI A4..A7 for AF_SPI1 use (function 5), int pin as not func, high speed, no pullups, not open drain, proper directions
|
|
for (i=4; i<=7; i++) {
|
|
gpio = gpioRequest(GPIO_PA(i));
|
|
gpioConfigAlt(gpio, GPIO_SPEED_HIGH, GPIO_PULL_NONE, GPIO_OUT_PUSH_PULL, GPIO_AF_SPI1);
|
|
gpioRelease(gpio);
|
|
}
|
|
|
|
wakeupGpio = gpioRequest(SH_INT_WAKEUP);
|
|
gpioConfigInput(wakeupGpio, GPIO_SPEED_HIGH, GPIO_PULL_NONE);
|
|
}
|
|
|
|
void blCleanup()
|
|
{
|
|
gpioRelease(wakeupGpio);
|
|
//reset units & return APB2 & AHB1 to initial state
|
|
RCC->APB2RSTR |= PERIPH_APB2_SPI1;
|
|
RCC->AHB1RSTR |= PERIPH_AHB1_GPIOA;
|
|
RCC->APB2RSTR &=~ PERIPH_APB2_SPI1;
|
|
RCC->AHB1RSTR &=~ PERIPH_AHB1_GPIOA;
|
|
RCC->APB2ENR = mOldApb2State;
|
|
RCC->AHB1ENR = mOldAhb1State;
|
|
}
|
|
|
|
bool blHostActive()
|
|
{
|
|
return !gpioGet(wakeupGpio);
|
|
}
|
|
|
|
void blConfigIo()
|
|
{
|
|
//config SPI
|
|
SPI->CR1 = 0x00000040; //spi is on, configured same as bootloader would
|
|
SPI->CR2 = 0x00000000; //spi is on, configured same as bootloader would
|
|
}
|
|
|
|
bool blSyncWait(uint32_t syncCode)
|
|
{
|
|
uint32_t nRetries;
|
|
//wait for sync
|
|
for (nRetries = 10000; nRetries; nRetries--) {
|
|
if (SPI->SR & 1) {
|
|
if (SPI->DR == syncCode)
|
|
break;
|
|
(void)SPI->SR; //re-read to clear overlfow condition (if any)
|
|
}
|
|
}
|
|
return nRetries > 0;
|
|
}
|
|
|
|
void __attribute__((noreturn)) __blEntry(void);
|
|
void __attribute__((noreturn)) __blEntry(void)
|
|
{
|
|
extern char __code_start[], __bss_end[], __bss_start[], __data_end[], __data_start[], __data_data[];
|
|
uint32_t appBase = ((uint32_t)&__code_start) & ~1;
|
|
|
|
//make sure we're the vector table and no ints happen (BL does not use them)
|
|
blDisableInts();
|
|
SCB->VTOR = (uint32_t)&BL;
|
|
|
|
//init things a little for the higher levels
|
|
memset(__bss_start, 0, __bss_end - __bss_start);
|
|
memcpy(__data_start, __data_data, __data_end - __data_start);
|
|
|
|
blMain(appBase);
|
|
|
|
//call OS with ints off
|
|
blDisableInts();
|
|
SCB->VTOR = appBase;
|
|
asm volatile(
|
|
"LDR SP, [%0, #0] \n"
|
|
"LDR PC, [%0, #4] \n"
|
|
:
|
|
:"r"(appBase)
|
|
:"memory", "cc"
|
|
);
|
|
|
|
//we should never return here
|
|
while(1);
|
|
}
|
|
|
|
static void blSpuriousIntHandler(void)
|
|
{
|
|
//BAD!
|
|
blReboot();
|
|
}
|
|
|
|
extern uint8_t __stack_top[];
|
|
uint64_t __attribute__ ((section (".stack"))) _STACK[BL_STACK_SIZE / sizeof(uint64_t)];
|
|
|
|
const struct BlVecTable __attribute__((section(".blvec"))) __BL_VEC =
|
|
{
|
|
.blStackTop = (uint32_t)&__stack_top,
|
|
.blEntry = &__blEntry,
|
|
.blNmiHandler = &blSpuriousIntHandler,
|
|
.blHardFaultHandler = &blSpuriousIntHandler,
|
|
.blMmuFaultHandler = &blSpuriousIntHandler,
|
|
.blBusFaultHandler = &blSpuriousIntHandler,
|
|
.blUsageFaultHandler = &blSpuriousIntHandler,
|
|
};
|