//-----------------------------------------------------------------------------
///
/// \brief  Functions for entering STM-Bootloader
///
///         Main goal is to perform several reboots before jumping to
///         bootloader. Reason is that it did not work with a single reboot and
///         checking in startup_xxx.s . Maybe i forgot to shut down something
///         but several reboots are reliable.
///
///         run checkBootLoader() at the very beginning of your C/C++ application
///         run signBootloader() before rebooting when you want to enter bootloader.
///
/// \date   20150611
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


//---Includes------------------------------------------------------------------


#include <stm/stm32_hal.h>       // NVIC_SystemReset();

// setNV
#include <biwak/nvic.h>
#include <stm/stm32_cortex.h>

//---Own------------------------------


//---Defines-------------------------------------------------------------------


//---Implementation------------------------------------------------------------


//*((unsigned long *)(&_estack-4)) = 0xDEADBEEF; // 64KB STM32F103
extern char _estack;
extern char _eram;

volatile uint32_t *bootloaderSign= (volatile uint32_t *) (unsigned long *) ( (&_eram) - 4 ); // (0x20004000-4); //
volatile uint32_t *bootloaderCounter= (volatile uint32_t *) (unsigned long *) ( (&_eram) - 8 ); // (0x20004000-8); //

void jumpBootloader()
{
   // Grep it: Only (all) STM32F0 have it
   #if defined(STM32F0)
   setNVInterrupt( EXTI0_1_IRQn, false, NVI_HIGH_PRIORITY);
   setNVInterrupt( EXTI2_3_IRQn, false, NVI_HIGH_PRIORITY);
   setNVInterrupt( EXTI4_15_IRQn, false, NVI_HIGH_PRIORITY);
   #endif
   setNVInterrupt( SysTick_IRQn, false, NVI_HIGH_PRIORITY);
   __GPIOA_CLK_DISABLE();
   __GPIOB_CLK_DISABLE();
   __GPIOC_CLK_DISABLE();
   __GPIOD_CLK_DISABLE();
   //__SYSCFG_CLK_DISABLE();
   HAL_RCC_DeInit();
   HAL_DeInit();
   __DSB();
   //__disable_irq();
   asm(
      "ldr    r0, =0x1FFFC800\n"
      "ldr   r2, [r0, #0]\n"
      "ldr   r3, [r0, #4]\n"
      "mov sp, r2\n"
      "bx r3\n": : :
   );
   return;
}


void reboot()
{
   // Grep it: Only (all) STM32F0 have it
   #if defined(STM32F0)
   setNVInterrupt( EXTI0_1_IRQn, false, NVI_HIGH_PRIORITY);
   setNVInterrupt( EXTI2_3_IRQn, false, NVI_HIGH_PRIORITY);
   setNVInterrupt( EXTI4_15_IRQn, false, NVI_HIGH_PRIORITY);
   #endif
   setNVInterrupt( SysTick_IRQn, false, NVI_HIGH_PRIORITY);
   __GPIOA_CLK_DISABLE();
   __GPIOB_CLK_DISABLE();
   __GPIOC_CLK_DISABLE();
   __GPIOD_CLK_DISABLE();
   __SYSCFG_CLK_DISABLE();
   HAL_RCC_DeInit();
   HAL_DeInit();
   __disable_irq();
   NVIC_SystemReset();
}


void checkBootLoader()
{
   while(0)
   {
      asm("nop;\n");
   }

   /*
   if(*bootloaderSign == 0xDEADBEEF)
   {
      if(0)
         for(int i1=0; i1<1000000; i1++)
         {
            asm("nop;\n");
         }
      *bootloaderSign = 0x0;
      jumpBootloader();

      if(*bootloaderCounter>=5)
      {
         *bootloaderSign = 0x0;
         jumpBootloader();
      }
      *bootloaderCounter++;
      asm("nop;\n");
      asm("nop;\n");
      asm("nop;\n");
      NVIC_SystemReset();
      //reboot();
   }
   else
      *bootloaderCounter=0;
   */

   if ( (*bootloaderSign == 0xDEADBEEF) && (*bootloaderCounter > 1))
   //if(*bootloaderSign == 0xDEADBEEF)
   {
      *bootloaderSign = 0x0;
      *bootloaderCounter = 0x0;
      asm("DSB;\n");
      jumpBootloader();
   }

   return;
}


void signBootloader()
{
   if(*bootloaderSign == 0xDEADBEEF)
   {
      *bootloaderCounter=(*bootloaderCounter)+1;
      *bootloaderCounter=(*bootloaderCounter)+1;
      asm("DSB;\n");
   }
   else
   {
      *bootloaderSign = 0xDEADBEEF;
      *bootloaderSign = 0xDEADBEEF;
      *bootloaderCounter = 0x0;
      *bootloaderCounter = 0x0;
      asm("DSB;\n");
   }

   return;
}


//---fin-----------------------------------------------------------------------
