| #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, |
| }; |