/******************** (C) COPYRIGHT 2015 STMicroelectronics ********************
* File Name          : DTM_boot.c
* Author             : AMS - VMA RF Application Team
* Version            : V1.0.0
* Date               : 25-December-2015
* Description        : BlueNRG-1 DTM boot routines
********************************************************************************
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "BlueNRG1_conf.h"
#include "bluenrg1_stack.h"
#include "DTM_config.h"

/* Private typedef -----------------------------------------------------------*/
typedef  PACKED(struct) devConfigS  {
  uint8_t  HS_crystal;
  uint8_t  LS_source;
  uint8_t  SMPS_management;
  uint8_t  Reserved;
  uint16_t HS_startup_time;
  uint16_t SlaveSCA;
  uint8_t  MasterSCA;
  uint32_t max_conn_event_length;
  uint8_t  Test_mode;
} devConfig_t;

/* Private define ------------------------------------------------------------*/

/* Device Configuration Registers */
#define ATB0_ANA_ENG_REG    0x3F
#define ATB1_ANA_ENG_REG    0x3E
#define RM1_DIG_ENG_REG     0x3C
#define CLOCK_LOW_ENG_REG   0x3B
#define CLOCK_HIGH_ENG_REG  0x3A
#define PMU_ANA_ENG_REG     0x39
#define CLOCK_ANA_USER_REG  0x34
#define NUMBER_CONFIG_BYTE  0x02
#define END_CONFIG          0x00

/* Device Configuration Register values */
#define LOW_FREQ_RO               0x1B
#define LOW_FREQ_XO               0x5B
#define HIGH_FREQ_16M             0x40
#define HIGH_FREQ_32M             0x44
#define SMPS_ON                   0x4C // (0x48 BOR enabled)
#define SMPS_OFF                  0x6C
#define SMPS_4_7uH_RM1            0x40
#define SMPS_4_7uH_PMU            0xBE
#define SMPS_10uH_RM1             0x20
#define SMPS_10uH_PMU             0xB2
#define USER_MODE_ATB0            0x00
#define USER_MODE_ATB1            0x30
#define LS_XTAL_MEAS_ATB0         0x37
#define LS_XTAL_MEAS_ATB1         0x34
#define HS_STARTUP_TIME_MEAS_ATB0 0x04
#define HS_STARTUP_TIME_MEAS_ATB1 0x34



/* Cold Start Configuration Table */
#define COLD_START_CONFIGURATION                         \
{                                                        \
  NUMBER_CONFIG_BYTE, ATB0_ANA_ENG_REG,   USER_MODE_ATB0,\
  NUMBER_CONFIG_BYTE, ATB1_ANA_ENG_REG,   USER_MODE_ATB1,\
  NUMBER_CONFIG_BYTE, RM1_DIG_ENG_REG,    SMPS_10uH_RM1, \
  NUMBER_CONFIG_BYTE, CLOCK_LOW_ENG_REG,  SMPS_ON,       \
  NUMBER_CONFIG_BYTE, CLOCK_HIGH_ENG_REG, HIGH_FREQ_16M, \
  NUMBER_CONFIG_BYTE, PMU_ANA_ENG_REG,    SMPS_10uH_PMU, \
  NUMBER_CONFIG_BYTE, CLOCK_ANA_USER_REG, LOW_FREQ_XO,   \
  END_CONFIG                                             \
}

/* Device Configuration value for struct deviceConfigS */
#define HS_CRYSTAL_16MHZ 0xFF
#define HS_CRYSTAL_32MHZ 0x00

#define LS_SOURCE_RO    0xFF
#define LS_SOURCE_32KHZ 0x00

#define SMSP_10uH     0xFF
#define SMSPS_4_7uH   0x00
#define SMPS_IND_OFF  0x01

#define USER_MODE               0xFF
#define LS_CRYSTAL_MEASURE      0x01
#define HS_STARTUP_TIME_MEASURE 0x02

/* Device Configuration Register values */ 
#define TX_RX_START_STOP_MEAS_ATB0 0x38
#define TX_RX_START_STOP_MEAS_ATB1 0x34

/* Device Configuration value for struct deviceConfigS */
#define TX_RX_START_STOP_MEASURE 0x03

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

devConfig_t deviceConfig;
volatile uint8_t cold_start_config[] = COLD_START_CONFIGURATION;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

static void coldStartConfiguration(void)
{
  /* High Speed Crystal Configuration */
  if (deviceConfig.HS_crystal == HS_CRYSTAL_32MHZ) {
    /* 32 MHz should be set in some way. This info is retrieved from flash info */
    cold_start_config[14] = HIGH_FREQ_32M;
  } else {
    cold_start_config[14] = HIGH_FREQ_16M;
  }

  /* Low Speed Crystal source */
  if (deviceConfig.LS_source == LS_SOURCE_32KHZ) {
    cold_start_config[20] = LOW_FREQ_XO;
  } else {
    cold_start_config[20] = LOW_FREQ_RO;
  }
  
  /* SMPS configuration */
  switch (deviceConfig.SMPS_management) {
  case SMSPS_4_7uH:
    {
      cold_start_config[11] = SMPS_ON;
      cold_start_config[8]  = SMPS_4_7uH_RM1;
      cold_start_config[17] = SMPS_4_7uH_PMU;
    }
    break;
  case SMPS_IND_OFF:
    {
      cold_start_config[11] = SMPS_OFF;
    }
    break;
  default:
    {
      cold_start_config[11] = SMPS_ON;
      cold_start_config[8]  = SMPS_10uH_RM1;
      cold_start_config[17] = SMPS_10uH_PMU;
    }
    break;
  }

  /* Test Mode */
  switch (deviceConfig.Test_mode) {
  case LS_CRYSTAL_MEASURE:
    {
      cold_start_config[2] = LS_XTAL_MEAS_ATB0;
      cold_start_config[5] = LS_XTAL_MEAS_ATB1;
    }
    break;
  case HS_STARTUP_TIME_MEASURE:
    {
      cold_start_config[2] = HS_STARTUP_TIME_MEAS_ATB0;
      cold_start_config[5] = HS_STARTUP_TIME_MEAS_ATB1;
    }
    break;
  case TX_RX_START_STOP_MEASURE:
    {
      cold_start_config[2] = TX_RX_START_STOP_MEAS_ATB0;
      cold_start_config[5] = TX_RX_START_STOP_MEAS_ATB1;
    }
    break;
  default:
    {
      cold_start_config[2] = USER_MODE_ATB0;
      cold_start_config[5] = USER_MODE_ATB1;
    }
    break;
  }
}


void DTM_SystemInit(void)
{
  uint32_t current_time;
  uint8_t *dev_config_addr;

  /* Remap the vector table */
  FLASH->CONFIG = FLASH_PREMAP_MAIN;

  /* Load device configuration from FLASH memory */
  /* The first 256 bytes are for stacklib_stored_device_id_data */
  dev_config_addr = (uint8_t *) 0x10067800 + 0x100;
  memcpy(&deviceConfig, dev_config_addr, sizeof(deviceConfig));

#if (HS_SPEED_XTAL==HS_SPEED_XTAL_16MHZ)
  if(deviceConfig.HS_crystal == HS_CRYSTAL_32MHZ) {
    deviceConfig.HS_crystal = 0xFF; // Set HS_CRYSTAL_16MHZ
    FLASH_ErasePage((0x10067800-_MEMORY_FLASH_BEGIN_)/N_BYTES_PAGE);
  }
#elif (HS_SPEED_XTAL==HS_SPEED_XTAL_32MHZ)
  if(deviceConfig.HS_crystal == HS_CRYSTAL_16MHZ) {
    uint32_t tmp32 = (((uint32_t)deviceConfig.Reserved)<<24) | (((uint32_t)deviceConfig.SMPS_management)<<16) | (((uint32_t)deviceConfig.LS_source)<<8) |  (((uint32_t)0x00)<<0);
    deviceConfig.HS_crystal = 0x00; // Set HS_CRYSTAL_32MHZ
    FLASH_ProgramWord(0x10067800 + 0x100, tmp32);
  }
#endif
  
  coldStartConfiguration();

  if (deviceConfig.HS_crystal == HS_CRYSTAL_32MHZ) {
    /* Set 32MHz_SEL bit in the System controller register */
    SYSTEM_CTRL->CTRL_b.MHZ32_SEL = 1;
  }

  /* Cold start configuration device */
  BLUE_CTRL->RADIO_CONFIG = 0x10000U | (uint16_t)((uint32_t)cold_start_config & 0x0000FFFFU);
  while ((BLUE_CTRL->RADIO_CONFIG & 0x10000) != 0);
  
  /* Wait until HS is ready. The slow clock period 
  * measurement is done automatically each time the
  * device enters in active2 state and the HS is ready.
  * The interrupt signals that a measurement is done.
  */
  while(CKGEN_BLE->CLK32K_IT == 0);
  CKGEN_BLE->CLK32K_IT = 1;
  CKGEN_BLE->CLK32K_COUNT = 23; //Restore the window length for slow clock measurement.
  CKGEN_BLE->CLK32K_PERIOD = 0;

  /* Wait until the RO or 32KHz is ready */
  current_time = *(volatile uint32_t *)0x48000010;
  while(((*(volatile uint32_t *)0x48000010)&0x10) == (current_time&0x10));
  
  if (deviceConfig.HS_crystal == HS_CRYSTAL_32MHZ) {
    /* AHB up converter command register write*/
    AHBUPCONV->COMMAND = 0x15;
  }
  
  /* Disable all the peripherals clock except NVM, SYSCTR and RNG */
  CKGEN_SOC->CLOCK_EN = 0x20066;
  
  /* Enable the IRQs */
  __enable_irq();
}

void DTM_StackInit(void)
{
  uint8_t ret;

  if (deviceConfig.max_conn_event_length > 1639344) {
    BlueNRG_Stack_Init_params.hardware_config.max_conn_event_length = 0xFFFFFFFF;
  } else {
    BlueNRG_Stack_Init_params.hardware_config.max_conn_event_length = deviceConfig.max_conn_event_length;
  }
  if (deviceConfig.SlaveSCA == 0xFFFF) {
    BlueNRG_Stack_Init_params.hardware_config.slave_sca = 500;
  } else {
    BlueNRG_Stack_Init_params.hardware_config.slave_sca = deviceConfig.SlaveSCA;
  }
  if (deviceConfig.MasterSCA == 0xFF) {
    BlueNRG_Stack_Init_params.hardware_config.master_sca = 0;
  } else {
    BlueNRG_Stack_Init_params.hardware_config.master_sca = deviceConfig.MasterSCA;
  }
  BlueNRG_Stack_Init_params.hardware_config.ls_source = deviceConfig.LS_source; 
  if (deviceConfig.HS_startup_time > 1354) {
    BlueNRG_Stack_Init_params.hardware_config.hs_startup_time = 0x54A;
  } else {
    BlueNRG_Stack_Init_params.hardware_config.hs_startup_time = deviceConfig.HS_startup_time;
  }
 
  ret = BlueNRG_Stack_Initialization(&BlueNRG_Stack_Init_params);
  if (ret != 0) {
    while(1);
  }
}

