/** @file rcc.h
 * Module handling Reset and Clocks Control (RCC).
 *
 * The module provides functions to configure clocks according to presets as
 * well as to enable/disable/reset peripherals.
 */

#ifndef _RCC_H_
#define _RCC_H_

//--includes--------------------------------------------------------------------

#include "stdint.h"


//--type definitions------------------------------------------------------------

/**
 * Available clock configuration presets
 */
enum RccPreset {
	RCC_PRESET_DEFAULT, //sane values, identical to reset config
	RCC_PRESET_SPEED,   //highest clocks, uses 8MHz HSE if available
};

/**
 * Available peripherals on the AHB bus. Note that some of these peripherals
 * may not be availables on all chips
 */
enum RccAhb {
	RCC_AHB_NONE  = 0,
	RCC_AHB_DMA1  = (0x1 <<  0),
	RCC_AHB_DMA2  = (0x1 <<  1),
	RCC_AHB_SRAM  = (0x1 <<  2),
	RCC_AHB_FLITF = (0x1 <<  4),
	RCC_AHB_CRC   = (0x1 <<  6),
	RCC_AHB_FSMC  = (0x1 <<  8),
	RCC_AHB_SDIO  = (0x1 << 10),
};

/**
 * Available peripherals on the APB1 bus. Note that some of these peripherals
 * may not be availables on all chips
 */
enum RccApb1 {
	RCC_APB1_NONE   = 0,
	RCC_APB1_TIM2   = (0x1 <<  0),
	RCC_APB1_TIM3   = (0x1 <<  1),
	RCC_APB1_TIM4   = (0x1 <<  2),
	RCC_APB1_TIM5   = (0x1 <<  3),
	RCC_APB1_TIM6   = (0x1 <<  4),
	RCC_APB1_TIM7   = (0x1 <<  5),
	RCC_APB1_TIM12  = (0x1 <<  6),
	RCC_APB1_TIM13  = (0x1 <<  7),
	RCC_APB1_TIM14  = (0x1 <<  8),
	RCC_APB1_WWDG   = (0x1 << 11),
	RCC_APB1_SPI2   = (0x1 << 14),
	RCC_APB1_SPI3   = (0x1 << 15),
	RCC_APB1_USART2 = (0x1 << 17),
	RCC_APB1_USART3 = (0x1 << 18),
	RCC_APB1_UART4  = (0x1 << 19),
	RCC_APB1_UART5  = (0x1 << 20),
	RCC_APB1_I2C1   = (0x1 << 21),
	RCC_APB1_I2C2   = (0x1 << 22),
	RCC_APB1_USB    = (0x1 << 23),
	RCC_APB1_CAN    = (0x1 << 25),
	RCC_APB1_BKP    = (0x1 << 27),
	RCC_APB1_PWR    = (0x1 << 28),
	RCC_APB1_DAC    = (0x1 << 29),
};

/**
 * Available peripherals on the APB2 bus. Note that some of these peripherals
 * may not be available on all chips
 */
enum RccApb2 {
	RCC_APB2_NONE  = 0,
	RCC_APB2_AFIO  = (0x1 <<  0),
	RCC_APB2_IOPA  = (0x1 <<  2),
	RCC_APB2_IOPB  = (0x1 <<  3),
	RCC_APB2_IOPC  = (0x1 <<  4),
	RCC_APB2_IOPD  = (0x1 <<  5),
	RCC_APB2_IOPE  = (0x1 <<  6),
	RCC_APB2_IOPF  = (0x1 <<  7),
	RCC_APB2_IOPG  = (0x1 <<  8),
	RCC_APB2_ADC1  = (0x1 <<  9),
	RCC_APB2_ADC2  = (0x1 << 10),
	RCC_APB2_TIM1  = (0x1 << 11),
	RCC_APB2_SPI1  = (0x1 << 12),
	RCC_APB2_TIM8  = (0x1 << 13),
	RCC_APB2_USART = (0x1 << 14),
	RCC_APB2_ADC3  = (0x1 << 15),
	RCC_APB2_TIM9  = (0x1 << 19),
	RCC_APB2_TIM10 = (0x1 << 20),
	RCC_APB2_TIM11 = (0x1 << 21),
};

enum RccRtcClockSrc {
	RCC_RTC_CLOCK_SRC_NONE = 0x0,
	RCC_RTC_CLOCK_SRC_LSE  = 0x1,
	RCC_RTC_CLOCK_SRC_LSI  = 0x2,
	RCC_RTC_CLOCK_SRC_HSE  = 0x3,
};

struct RccClocks {
	uint32_t ahb_freq;
	uint32_t apb1_freq;
	uint32_t apb2_freq;
	uint32_t tim_freq;
};


//--functions-------------------------------------------------------------------

/**
 * Configures the clocks and buses according to the given preset. Peripheral
 * states are kept during the change and additional reconfiguration are
 * handled automatically for peripherals that rely on a clock timings (timers,
 * watchdogs, ...)
 */
void rcc_configure(enum RccPreset preset);

/**
 * Configures the Low Speed Internal (LSI) oscillator for low power
 * applications.
 */
void rcc_configure_lsi(bool enable);

/**
 * Enables peripherals on the different buses. The enums values can used as
 * masks to enable multiple peripherals at the same time. Invalid values will be
 * ignored.
 */
void rcc_enable(enum RccAhb ahb_mask, enum RccApb1 apb1_mask,
		enum RccApb2 apb2_mask);

/**
 * Disables peripherals on the different buses. The enums values can used as
 * masks to disable multiple peripherals at the same time. Invalid values will
 * be ignored.
 */
void rcc_disable(enum RccAhb ahb_mask, enum RccApb1 apb1_mask,
		enum RccApb2 apb2_mask);

/**
 * Resets peripherals on the different buses. The enums values can used as
 * masks to reset multiple peripherals at the same time. Invalid values will
 * be ignored.
 */
void rcc_reset(enum RccApb1 apb1_mask, enum RccApb2 apb2_mask);

void rcc_get_clocks(struct RccClocks* clocks);


#endif //_RCC_H_

