/*******************************************************************************
* Project:      Amped'RF Bluetooth
* File name:    GKI_rcc.c
* Description:  RCC abstraction layer
*
* Copyright (c) 2013 Amped'RF Inc., AR Technology  All rights reserved.
*******************************************************************************/
#include "SDK_Common.h"
#include "GKI_lib.h"



//------------------------------------------------------------------------------
// Function:    GKI_rcc_ConfigMCO
// Description: Configure MCO (PA8)
//------------------------------------------------------------------------------
void GKI_rcc_ConfigMCO(void)
{
#ifdef DISABLE_MCO

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_StructInit(&GPIO_InitStruct);          // Default is an input
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

#else
 #if !(defined(BT24)||defined(BT53H))
    // Turn on HSE

	//
    //RCC_HSEConfig(RCC_HSE_ON);
    //RCC_WaitForHSEStartUp();
	RCC_HSEConfig(RCC_HSE_OFF);
	while(RESET != RCC_GetFlagStatus(RCC_FLAG_HSERDY));

	RCC_HSEConfig(RCC_HSE_ON);
	while(RESET == RCC_GetFlagStatus(RCC_FLAG_HSERDY));


    // Setup GPIO for MCO
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin    = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Mode   = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed  = GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_OType  = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd   = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8,  GPIO_AF_MCO);

    // Set HSE source for MCO1
    RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);

    // Wait for stability
    //RCC_WaitForHSEStartUp();
#endif
#endif
}



#if defined(STM32F2xx)
#ifdef USE_HSE_CLOCK
tPLL_Table PLL_Table[]={
     1,  true,   20,  413,   8,  RCC_SYSCLK_Div64,  1,  1,   115200,    // PClk =   1,048,632 Hz
    13,  false,   0,    0,   0,  RCC_SYSCLK_Div2,   1,  1,   230400,    // PClk =  13,000,000 Hz
    26,  false,   0,    0,   0,  RCC_SYSCLK_Div1,   1,  1,   921600,    // PClk =  26,000,000 Hz
    52,  true,   26,  206,   2,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk =  52,166,666 Hz
   100,  true,   13,  313,   6,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk = 104,333,333 Hz
   120,  true,   13,  240,   4,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk = 120,000,000 Hz
   255,  true,   13,  240,   4,  RCC_SYSCLK_Div1,   1,  1,  3250000
};

#else // use HSI clock
tPLL_Table PLL_Table[]={
     1,  true,    9,  302,   8,  RCC_SYSCLK_Div64,  1,  1,   115200,    // PClk =   1,048,610 Hz
    13,  true,   16,  313,   6,  RCC_SYSCLK_Div4,   1,  1,   230400,    // PClk =  13,041,666 Hz
    26,  true,    8,  313,   6,  RCC_SYSCLK_Div4,   1,  1,   921600,    // PClk =  26,083,333 Hz
    52,  true,   16,  313,   6,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk =  52,166,666 Hz
   100,  true,    8,  313,   6,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk = 104,333,333 Hz
   120,  true,    8,  240,   4,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk = 120,000,000 Hz
   255,  true,    8,  240,   4,  RCC_SYSCLK_Div1,   1,  1,  3250000
};
#endif

#elif defined(STM32F4xx)
#ifdef USE_HSE_CLOCK
tPLL_Table PLL_Table[]={
     1,  true,   20,  413,   8,  RCC_SYSCLK_Div64,  1,  1,   115200,    // PClk =   1,048,632 Hz
    13,  false,   0,    0,   0,  RCC_SYSCLK_Div2,   1,  1,   230400,    // PClk =  13,000,000 Hz
    26,  false,   0,    0,   0,  RCC_SYSCLK_Div1,   1,  1,   921600,    // PClk =  26,000,000 Hz
  //52,  true,   26,  206,   2,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk =  52,166,666 Hz
    52,  true,   26,  208,   4,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk =  52,166,666 Hz
    84,  true,   26,  168,   2,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk =  52,166,666 Hz
   100,  true,   13,  313,   6,  RCC_SYSCLK_Div1,   1,  1,  3250000,    // PClk = 104,333,333 Hz
   255,  true,   13,  313,   6,  RCC_SYSCLK_Div1,   1,  1,  3250000
};

#else // use HSI clock
tPLL_Table PLL_Table[]={
//  MHz    PLL    M     N     P           AHB           APB1    APB2      MaxBaud//For HSI [-1% 1%] RF[-2.5%, 1.5%]
     1,	  true,  40,   240,   6,    RCC_SYSCLK_Div16,     1,      1,       115200,//115200,//For theory 38400
     2,	  true,  40,   240,   6,    RCC_SYSCLK_Div8,      1,      1,       115200,//115200,//For theory 57600
     4,   true,  40,   240,   6,    RCC_SYSCLK_Div4,      1,      1,       230400,//230400,//For theory 115200
     8,   true,  40,   240,   6,    RCC_SYSCLK_Div2,      1,      1,       230400,//230400,//For theory 230400
     12,  true,  40,   240,   8,    RCC_SYSCLK_Div1,      1,      1,       460800,//230400,//For theory 460800
     16,  false, 40,   240,   6,    RCC_SYSCLK_Div1,      1,      1,       921600,//921600,//For theory 460800
     20,  true,  32,   240,   6,    RCC_SYSCLK_Div1,      1,      1,       921600,//921600,//For theory 460800
     24,  true,  40,   240,   4,    RCC_SYSCLK_Div1,      1,      1,       921600,//921600,//For theory 921600
     28,  true,  40,   280,   4,    RCC_SYSCLK_Div1,      1,      1,       921600,//921600,//For theory 921600
     32,  true,  20,   240,   6,    RCC_SYSCLK_Div1,      1,      1,       2000000,//921600,//For theory 921600
     36,  true,  8,    228,   6,    RCC_SYSCLK_Div2,      1,      1,       2000000,//921600,//For theory 2000000
     42,  true,  16,   336,   8,    RCC_SYSCLK_Div1,      1,      1,       2000000,//921600,//For theory 2000000

     64,  true,  30,   240,   2,    RCC_SYSCLK_Div1,      1,      1,       1843200,//1843200,//For theory 3000000
     84,  true,  8,    336,   8,    RCC_SYSCLK_Div1,      1,      1,       2000000,//2000000,//For theory 3000000

     255, true,  8,    336,   8,    RCC_SYSCLK_Div1,      2,      1,       921600
};
#endif
#endif

#define PLL_TBL_SIZE    sizeof(PLL_Table) / sizeof(tPLL_Table)
#define PLLQ            4

u16 pll_tbl_size = PLL_TBL_SIZE;


/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
void GKI_rcc_SetSysClock(uint32 hz)
{

    // Make sure hz is in the correct range
    if (hz <= 200) {
        hz *= 1000000;
    }

#ifdef USE_HSE_CLOCK
    int i;

	while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET)
    {}  // Wait till HSE is ready
    RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
	while(RCC_GetSYSCLKSource() != 0x04);

    for (i=0; i<PLL_TBL_SIZE; i++ )
    {
        if (hz <= PLL_Table[i].mhz * 1000000)
        {
            RCC_PLLCmd(DISABLE);
			while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == SET);

            if (PLL_Table[i].usePLL)
            {
                RCC_PLLConfig(RCC_PLLSource_HSE,
                    PLL_Table[i].M, PLL_Table[i].N, PLL_Table[i].P, PLLQ);

                RCC_PLLCmd(ENABLE);
                while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
                {}  // Wait till PLL is ready

                RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
				while(RCC_GetSYSCLKSource() != 0x08);
            }
            //RCC_HCLKConfig(PLL_Table[i].AHB_Prescale);

		    RCC_HCLKConfig(RCC_SYSCLK_Div1);
            RCC_PCLK1Config(RCC_HCLK_Div1);
            RCC_PCLK2Config(RCC_HCLK_Div1);

            // 32 KHz BTC Clock
            GKI_rcc_BT_ClockConfig(TM_BT_CLOCK_SPEED);
            break;
        }
    }

#else // use HSI clock
    int i;

	while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET)
    {}  // Wait till HSI is ready
    RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
	while(RCC_GetSYSCLKSource() != 0x00);

    for (i=0; i<PLL_TBL_SIZE; i++ )
    {
        if (hz <= PLL_Table[i].mhz * 1000000)
        {
            RCC_PLLCmd(DISABLE);
			while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == SET);

            if (PLL_Table[i].usePLL)
            {
                RCC_PLLConfig(RCC_PLLSource_HSI,
                    PLL_Table[i].M, PLL_Table[i].N, PLL_Table[i].P, PLLQ);

                RCC_PLLCmd(ENABLE);
                while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
                {}  // Wait till PLL is ready

                RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

				while(RCC_GetSYSCLKSource() != 0x08);
            }
            RCC_HCLKConfig(PLL_Table[i].AHB_Prescale);

            RCC_PCLK1Config(PLL_Table[i].APB1Div == 1 ? RCC_HCLK_Div1 : RCC_HCLK_Div2);
            RCC_PCLK2Config(PLL_Table[i].APB2Div == 1 ? RCC_HCLK_Div1 : RCC_HCLK_Div2);

            // 32 KHz BTC Clock
#if !defined(BT24)
            GKI_rcc_BT_ClockConfig(TM_BT_CLOCK_SPEED);//generatge 32k here.
#endif
            break;
        }
    }

#endif
}



//------------------------------------------------------------------------------
// Function:    GKI_rcc_MaxBaudrate
// Description: Return max baudrate for MHz
//------------------------------------------------------------------------------
uint32 GKI_rcc_MaxBaudrate(uint8 mhz)
{
    for (int i=0; i<PLL_TBL_SIZE; i++ )
    {
        mhz = mhz & ~1;
        if (mhz == PLL_Table[i].mhz)
            return PLL_Table[i].MaxBaudrate;
    }
    return 115200;
}


//------------------------------------------------------------------------------
// Function:    GKI_rcc_MinCpuMHz
// Description: Return max baudrate for MHz
//------------------------------------------------------------------------------
uint8 GKI_rcc_MinCpuMHz(uint32 baudrate)
{
    for (int i=0; i<PLL_TBL_SIZE; i++ )
    {
        if (PLL_Table[i].MaxBaudrate >= baudrate)
            return PLL_Table[i].mhz;
    }
    return 120;
}




//----------------------------------------------------------------------
// Function:    GKI_rcc_BT_ClockConfig
// Description: Setup BT clock (LPO) using PWM to 32 KHz
//              Assume 32MHz, Pin is PB0
//----------------------------------------------------------------------
void GKI_rcc_BT_ClockConfig(uint32 Hz)
{
//#ifdef DISABLE_LPO
if (TM_USE_EXT_LPO) {  //YAO


    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_StructInit(&GPIO_InitStruct);          // Default is an input
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//#else
else
{
    RCC_ClocksTypeDef RCC_Clocks;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    uint32 Period;
    uint32 freq;

    // TIM3 clock enable
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    // PB0 Configuration: TIM3 as alternate function push-pull
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP ;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    //GKI_io_Config(IO_HCI_LPCLK, GKI_IO_AF_PP | GKI_IO_Speed_50MHz);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3);


    // Get Clock frequency and determine period divisor for 32K
    RCC_GetClocksFreq(&RCC_Clocks);
    freq = RCC_Clocks.PCLK1_Frequency;

    // If APB1 prescaler is not Div1 then multiply freq by 2
    u32 APB1_PreScaler = (RCC->CFGR & RCC_CFGR_PPRE1);
    if (APB1_PreScaler != RCC_CFGR_PPRE1_DIV1 ) {
        freq *= 2;
    }

    // Calculate Period
    Period = (freq / Hz) - 1;

    // Time base configuration
    memset(&TIM_TimeBaseStructure, 0, sizeof(TIM_TimeBaseStructure));
    TIM_TimeBaseStructure.TIM_Period        = Period; // -1;
    TIM_TimeBaseStructure.TIM_Prescaler     = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);


    // Setup PWM
    memset(&TIM_OCInitStructure, 0, sizeof(TIM_OCInitStructure));
    TIM_OCInitStructure.TIM_OCMode          = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState     = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse           = Period/2;
    TIM_OCInitStructure.TIM_OCPolarity      = TIM_OCPolarity_High;
    TIM_OC3Init(TIM3, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

    TIM_ARRPreloadConfig(TIM3, ENABLE);

    TIM_Cmd(TIM3, ENABLE);
//#endif
}

}



//------------------------------------------------------------------------------
// Function:    GKI_rcc_ValidateCpuMHz
// Description: Return true if MHz is valid
//------------------------------------------------------------------------------
bool GKI_rcc_ValidateCpuMHz(uint8 mhz)
{
    for (int i=0; i<PLL_TBL_SIZE-1; i++ )
    {
        if (PLL_Table[i].mhz == mhz)
            return true;
    }
    return false;
}




//------------------------------------------------------------------------------
// Function:    GKI_rcc_ValidateCpuMHz
// Description: Return true if MHz is valid
//------------------------------------------------------------------------------
char * GKI_rcc_ValidMhzText(void)
{
    static char text[4*PLL_TBL_SIZE];
    char num[8];

    for (int i=0; i<PLL_TBL_SIZE-1; i++ )
    {
        if (i==0)
            *text = 0;
        else
            strcat(text, ", ");

        sprintf(num, "%d", PLL_Table[i].mhz);
        strcat(text, num);
    }

    return text;
}






//------------------------------------------------------------------------------
// Function:    GKI_rcc_CheckExtLPO
// Description: Return true if External LPO exists
//------------------------------------------------------------------------------
bool GKI_rcc_CheckExtLPO(void)
{
#if 0//defined(BT24)
    return false;
#else
    static bool initialized=false;
    static bool exists=false;
    if (!initialized)
    {
        bool BitState;
		bool BitStatePre;
		uint16 CpatureTimes = 0;

        switch (*(uint8 *)TM_GetFlashReadPtr(VAR_ID_UseExtLPO)) {
        case 2:
            GKI_io_Config   (IO_LPOCLK_EN, GKI_IO_OUT_PP);
            GKI_io_WriteBit (IO_LPOCLK_EN, 1);
            GKI_io_Config   (IO_HCI_LPCLK, GKI_IO_IPU);
            BitStatePre = GKI_io_ReadBit(IO_HCI_LPCLK);
			GKI_tim_SetDownTimer(1000);  //wait 1 second; unit:1ms
           while (GKI_tim_GetDownTimer()){
		   		BitState = GKI_io_ReadBit(IO_HCI_LPCLK);
		   		if(BitState != BitStatePre){
					BitStatePre = BitState;
					CpatureTimes++;
					if(CpatureTimes > 88){
						exists = true;
                    	break;
					}
				}
            }
            break;
        case 1:
            GKI_io_Config(IO_HCI_LPCLK, GKI_IO_IPU);
            BitStatePre = GKI_io_ReadBit(IO_HCI_LPCLK);
            GKI_tim_SetDownTimer(2000);//wait 2 second; unit:2ms
            while (GKI_tim_GetDownTimer()) {
                BitState = GKI_io_ReadBit(IO_HCI_LPCLK);
		   		if(BitState != BitStatePre){
					BitStatePre = BitState;
					CpatureTimes++;
					if(CpatureTimes > 88){
						exists = true;
                    	break;
					}
				}
            }
            exists = true;
            break;
        default:
            exists = false;
            break;
        }
		GKI_io_Config(IO_HCI_LPCLK, GKI_IO_IN_FLOATING);
        initialized = true;
    }
    return exists;
#endif
}




//------------------------------------------------------------------------------
// Function Name  : GKI_rcc_MainClockUsesHSE
// Description    : Returns true if main clock is HSE
//------------------------------------------------------------------------------
bool GKI_rcc_MainClockUsesHSE(void)
{
    return (RCC_GetSYSCLKSource() == 0x04);
}



//------------------------------------------------------------------------------
// Function Name  : GKI_rcc_HSEConfig
// Description    : Turn on or off the HSE
//------------------------------------------------------------------------------
void GKI_rcc_HSEConfig(bool en)
{
    switch (en) {
    case DISABLE:
        RCC_HSEConfig(RCC_HSE_OFF);
        RCC_WaitForHSEStartUp();
        break;

    case ENABLE:
    default:
        RCC_HSEConfig(RCC_HSE_ON);
        RCC_WaitForHSEStartUp();
        break;
    }
}


//------------------------------------------------------------------------------
// Function Name  : GKI_rcc_GetHSICalibrationValue
// Description    : Get HSI Calibration Value
//------------------------------------------------------------------------------
uint8 GKI_rcc_GetHSICalibrationValue(void)
{
    return (RCC->CR >>3) & 0x1F;
}


//------------------------------------------------------------------------------
// Function Name  : GKI_rcc_AdjustHSICalibrationValue
// Description    : Adjust HSI Calibration Value
//------------------------------------------------------------------------------
void GKI_rcc_AdjustHSICalibrationValue(uint8 n)
{
    RCC_AdjustHSICalibrationValue(n);
}


//--------------------------------------------------------------
//