#ifndef __RCC_H__
#define __RCC_H__

#include "types.h"
#include "base_addresses.h"
#include "bit_access.h"
#include "bitwise_enums.h"
#include "peripherals.h"

// ===============================================================================
//      Internal/external clocks, PLL, CSS and MCO configuration functions
// ===============================================================================

//  This class provide functions allowing to configure the internal/external clocks,
//  PLLs, CSS and MCO pins.
//
//  1. HSI (high-speed internal), 16 MHz factory-trimmed RC used directly or through
//     the PLL as System clock source.
//
//  2. LSI (low-speed internal), 32 KHz low consumption RC used as IWDG and/or RTC
//     clock source.
//
//  3. HSE (high-speed external), 4 to 26 MHz crystal oscillator used directly or
//     through the PLL as System clock source. Can be used also as RTC clock source.
//
//  4. LSE (low-speed external), 32 KHz oscillator used as RTC clock source.
//
//  5. PLL (clocked by HSI or HSE), featuring two different output clocks:
//      - The first output is used to generate the high speed system clock (up to 168 MHz)
//      - The second output is used to generate the clock for the USB OTG FS (48 MHz),
//        the random analog generator (<=48 MHz) and the SDIO (<= 48 MHz).
//
//  6. PLLI2S (clocked by HSI or HSE), used to generate an accurate clock to achieve
//     high-quality audio performance on the I2S interface.
//
//  7. CSS (Clock security system), once enable and if a HSE clock failure occurs
//     (HSE used directly or through PLL as System clock source), the System clock
//     is automatically switched to HSI and an interrupt is generated if enabled.
//     The interrupt is linked to the Cortex-M4 NMI (Non-Maskable Interrupt)
//     exception vector.
//
//  8. MCO1 (microcontroller clock output), used to output HSI, LSE, HSE or PLL
//     clock (through a configurable prescaler) on PA8 pin.
//
//  9. MCO2 (microcontroller clock output), used to output HSE, PLL, SYSCLK or PLLI2S
//     clock (through a configurable prescaler) on PC9 pin.

class RCC
{
	public:
		enum HS_ExternalOscillatorState	{ HSE_OFF  = 0, HSE_ON = 1, HSE_BYPASS = 5 };
		enum HS_InternalOscillatorState	{ DISABLED = 0, ENABLED = 1 };
		enum LS_ExternalOscillatorState	{ LSE_OFF  = 0, LSE_ON = 1, LSE_BYPASS = 4 };
		enum PLL_ClockSources 			{ HS_InternalOscillator = 0, HS_ExternalOscillator = 1 };
		enum SystemClockSources 		{ Internal = 0, External = 1, PhaseLockedLoop = 2 };
		enum PLL_State 					{ PLL_OFF = 0, PLL_ON = 1};
		enum I2S_PLL_State 				{ I2S_PLL_OFF = 0, I2S_PLL_ON = 1};
		enum ClockSecurityState			{ SECURITY_OFF = 0, SECURITY_ON = 1};
		enum MCO1_ClockSource			{ MCO1_HSI = 0   , MCO1_LSE = 1, MCO1_HSE = 2, MCO1_PLL = 3};
		enum MCO2_ClockSource			{ MCO2_SYSCLK = 0, MCO2_I2SCLK , MCO2_HSE = 2, MCO2_PLL = 3};

		enum PLL_Divisor	  			{ PLL_DividedByTwo = 0, PLL_DividedByFour = 1,
			                              PLL_DividedBySix = 2, PLL_DividedByEight = 3 };

		enum SYSCLK_Divisor				{ SYSCLK = 0, SYSCLK_DividedBy2 = 8, SYSCLK_DividedBy4 = 9, SYSCLK_DividedBy8 = 10,
			                              SYSCLK_DividedBy16 =  11, SYSCLK_DividedBy64 = 12, SYSCLK_DividedBy128 = 13,
			                              SYSCLK_Dividedby256 = 14, SYSCLK_DividedBy512 = 15 };

		enum APB_Divisor				{ AHBCLOCK = 0, AHBCLOCK_DividedBy2 = 4, AHBCLOCK_DividedBy4 = 5,
										  AHBCLOCK_DividedBy8 = 6, AHBCLOCK_DividedBy16 = 7 };

		~RCC(void);
		enum { address = RCC_BASE };
		void *operator new(size_t) { return reinterpret_cast<void *> (address); }
		void Configure_HS_ExternalOscillator(HS_ExternalOscillatorState state);
		bool Is_HS_ExternalOscillatorReady(uint32_t timeout);

		void Configure_HS_InternalOscillator(HS_InternalOscillatorState state);
		void Adjust_HS_InternalOscillatorTrim(uint32_t trim);
		bool Is_HS_InternalOscillatorReady(uint32_t timeout);

		void Configure_LS_ExternalOscillator(LS_ExternalOscillatorState state);
		bool Is_LS_ExternalOscillatorReady(uint32_t timeout);

		void Initialize_PLL(PLL_ClockSources clksrc, uint32_t prescalar_M, uint32_t multiplier_N,
				            PLL_Divisor pll_Q, uint32_t  OTG_FS_SDIO_RNG_divisor_Q);
		void Configure_PLL(PLL_State state);
		bool Is_PLL_Ready(uint32_t timeout);
		void Configure_I2S_PLL(I2S_PLL_State state);
		bool Is_I2S_PLL_Ready(uint32_t timeout);
		void ConfigureClockSecurity(ClockSecurityState css);
		void ConfigureMCO1(MCO1_ClockSource clksrc, uint32_t divisor);
		void ConfigureMCO2(MCO2_ClockSource clksrc, uint32_t divisor);

		void SetSystemClockSource(SystemClockSources clksrc);
		SystemClockSources GetSystemClockSource(void);
		void Select_AHB_ClockDivider(SYSCLK_Divisor divisor);
		void Select_APB1_ClockDivider(uint32_t divisor);
		void Select_APB2_ClockDivider(uint32_t divisor);
		void GetClocks(uint32_t& SYS_ClockInHz , uint32_t& AHB_ClockInHz,
				       uint32_t& APB1_ClockInHz, uint32_t& APB2_ClockInHz);

		void ResetPeripherals(AHB1_PERIPHERALS_RESET bits, BitOperation op);
		void ResetPeripherals(AHB2_PERIPHERALS_RESET bits, BitOperation op);
		void ResetPeripherals(AHB3_PERIPHERALS_RESET bits, BitOperation op);
		void ResetPeripherals(APB1_PERIPHERALS_RESET bits, BitOperation op);
		void ResetPeripherals(APB2_PERIPHERALS_RESET bits, BitOperation op);

		void EnableClocks(AHB1_PERIPHERALS_ENABLED bits, BitOperation op);
		void EnableClocks(AHB2_PERIPHERALS_ENABLED bits, BitOperation op);
		void EnableClocks(APB3_PERIPHERALS_ENABLED bits, BitOperation op);
		void EnableClocks(APB1_PERIPHERALS_ENABLED bits, BitOperation op);
		void EnableClocks(APB2_PERIPHERALS_ENABLED bits, BitOperation op);

		void EnableClocksInSleepMode(AHB1_PERIPHERALS_ENABLED_DURING_SLEEP bits, BitOperation op);
		void EnableClocksInSleepMode(AHB2_PERIPHERALS_ENABLED_DURING_SLEEP bits, BitOperation op);
		void EnableClocksInSleepMode(AHB3_PERIPHERALS_ENABLED_DURING_SLEEP bits, BitOperation op);
		void EnableClocksInSleepMode(APB1_PERIPHERALS_ENABLED_DURING_SLEEP bits, BitOperation op);
		void EnableClocksInSleepMode(APB2_PERIPHERALS_ENABLED_DURING_SLEEP bits, BitOperation op);

	private:

		__IO uint32_t CR;            // RCC clock control register,                                  Offset: 0x00
		__IO uint32_t PLLCFGR;       // RCC PLL configuration register,                              Offset: 0x04
		__IO uint32_t CFGR;          // RCC clock configuration register,                            Offset: 0x08
		__IO uint32_t CIR;           // RCC clock interrupt register,                                Offset: 0x0C
		__IO uint32_t AHB1RSTR;      // RCC AHB1 peripheral reset register,                          Offset: 0x10
		__IO uint32_t AHB2RSTR;      // RCC AHB2 peripheral reset register,                          Offset: 0x14
		__IO uint32_t AHB3RSTR;      // RCC AHB3 peripheral reset register,                          Offset: 0x18
		__IO uint32_t RESERVED0;     // Reserved, 0x1C                                                                    */
		__IO uint32_t APB1RSTR;      // RCC APB1 peripheral reset register,                          Offset: 0x20
		__IO uint32_t APB2RSTR;      // RCC APB2 peripheral reset register,                          Offset: 0x24
		__IO uint32_t RESERVED1[2];  // Reserved, 0x28-0x2C                                                               */
		__IO uint32_t AHB1ENR;       // RCC AHB1 peripheral clock enable register                    Offset: 0x30
		__IO uint32_t AHB2ENR;       // RCC AHB2 peripheral clock enable register                    Offset: 0x34
		__IO uint32_t AHB3ENR;       // RCC AHB3 peripheral clock enable register                    Offset: 0x38
		__IO uint32_t RESERVED2;     // Reserved, 0x3C                                                                    */
		__IO uint32_t APB1ENR;       // RCC APB1 peripheral clock enable register                    Offset: 0x40
		__IO uint32_t APB2ENR;       // RCC APB2 peripheral clock enable register                    Offset: 0x44
		__IO uint32_t RESERVED3[2];  // Reserved, 0x48-0x4C                                                               */
		__IO uint32_t AHB1LPENR;     // RCC AHB1 peripheral clock enable in low power mode register, Offset: 0x50
		__IO uint32_t AHB2LPENR;     // RCC AHB2 peripheral clock enable in low power mode register, Offset: 0x54
		__IO uint32_t AHB3LPENR;     // RCC AHB3 peripheral clock enable in low power mode register, Offset: 0x58
		__IO uint32_t RESERVED4;     // Reserved, 0x5C                                                                    */
		__IO uint32_t APB1LPENR;     // RCC APB1 peripheral clock enable in low power mode register, Offset: 0x60
		__IO uint32_t APB2LPENR;     // RCC APB2 peripheral clock enable in low power mode register, Offset: 0x64
		__IO uint32_t RESERVED5[2];  // Reserved, 0x68-0x6C                                                               */
		__IO uint32_t BDCR;          // RCC Backup domain control register,                          Offset: 0x70
		__IO uint32_t CSR;           // RCC clock control & status register,                         Offset: 0x74
		__IO uint32_t RESERVED6[2];  // Reserved, 0x78-0x7C                                                               */
		__IO uint32_t SSCGR;         // RCC spread spectrum clock generation register,               Offset: 0x80
		__IO uint32_t PLLI2SCFGR;    // RCC PLLI2S configuration register,                           Offset: 0x84
	};

#endif // __RCC_H__
