// ==========================================================================
// flash.c
// (c) 2020, Aurel Dumitru
//
// Description:
// Flash dual boot implementation
// =========================================================================

#include "flash.h"
#include "main.h"

FLASH_EraseInitTypeDef      EraseInitStruct;
FLASH_OBProgramInitTypeDef  OBInit;
uint8_t						Flash_CurrentBank;

void Flash_Init(void)
{
    /* Allow Access to Flash control registers and user Flash */
    HAL_FLASH_Unlock();
   	Flash_CurrentBank = 1;
    if ((FLASH->OPTCR) & FLASH_OPTCR_SWAP_BANK)
    	Flash_CurrentBank = 2;
}


uint8_t Flash_EraseUnusedBank(void)
{
	uint32_t Error;

 	EraseInitStruct.TypeErase   = FLASH_TYPEERASE_MASSERASE;
   	EraseInitStruct.Banks		= FLASH_BANK_2;    	//erase bank 2 always
    if (HAL_FLASHEx_Erase(&EraseInitStruct, &Error) != HAL_OK)
    	return 0;
    return 1;
}


uint8_t Flash_ProgrammUnusedBank(uint32_t Src, uint32_t Size)
{
   	uint32_t  FlashAddr	= FLASH_BANK2_BASE; // program always bank 2
   	uint32_t* PtrFlash	= (uint32_t*)FLASH_BANK2_BASE;
   	uint32_t* PtrRam	= (uint32_t*)Src;
	uint32_t  SrcEnd	= Src + ((Size+15) & 0xFFFFFFF0);

    // Program a 2 double-word (64-bit) at a specified address
    while (Src < SrcEnd)
    {
    	if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, FlashAddr, (uint64_t)Src) != HAL_OK)
        	return 0;
    	Src       += 2*sizeof(uint64_t);
		FlashAddr += 2*sizeof(uint64_t);
    }

    // Verify the written content
    Size = (FlashAddr - FLASH_BANK2_BASE)/4;
    while (Size--)
    	if ((*PtrFlash++) != (*PtrRam++))
    		return 0;

    // This point is reached when everything is ok
    return 1;
}


void Flash_ChangeBootBank(void)
{
	OBInit.OptionType = OPTIONBYTE_USER;
	OBInit.USERType   = OB_USER_SWAP_BANK;
	OBInit.USERConfig = OB_SWAP_BANK_DISABLE;   	// bank 1
	if (Flash_CurrentBank == 1)
    	OBInit.USERConfig = OB_SWAP_BANK_ENABLE;   	// bank 2

    HAL_FLASH_OB_Unlock();
    if (HAL_FLASHEx_OBProgram(&OBInit) == HAL_OK)
    	HAL_FLASH_OB_Launch();
}


uint8_t Flash_GetCurrentBank(void)
{
	return Flash_CurrentBank;
}
