/** 
 * \file clocks.h
 * \brief clocks header file
 * \details File contains structures, enumerations and functions declarations for clock sources
 *
 * \author Juraj Golej
 * \version 1.0
 *
 * \todo [clocks] USB clock control
 */


#ifndef CLOCKS_H_
#define CLOCKS_H_

#include "sam.h"
#include <inttypes.h>
#include <clocks/oscillators/oscillators.h>
#include <utils/retcode/retcode.h>

/** 
 * \brief Oscillators sources enumeration
 */
enum PMC_osc_source {
	PMC_OSC_SLCK,	///< Slow clock
	PMC_OSC_MAINCK,	///< Main clock
	PMC_OSC_PLLA,	///< PLLA clock
	PMC_OSC_UPLL,	///< UPLL clock
	PMC_OSC_MCK,	///< Master clock
}; /* enum PMC_osc_source */

/** 
 * \brief Prescaler values enumeration
 */
enum PMC_prescaler {
	PMC_PRES_1,		///< Prescaler 1 (fCLK)
	PMC_PRES_2,		///< Prescaler 2 (fCLK/2)
	PMC_PRES_4,		///< Prescaler 4 (fCLK/4)
	PMC_PRES_8,		///< Prescaler 8 (fCLK/8)
	PMC_PRES_16,	///< Prescaler 16 (fCLK/16)
	PMC_PRES_32,	///< Prescaler 32 (fCLK/32)
	PMC_PRES_64,	///< Prescaler 64 (fCLK/64)
	PMC_PRES_3,		///< Prescaler 3 (fCLK/3)
}; /* enum PMC_prescaler */

/** 
 * \brief Divider values enumeration
 */
enum PMC_divider {
	PMC_DIV_1,		///< Divider 1 (fCLK)
	PMC_DIV_2,		///< Divider 2 (fCLK/2)
	PMC_DIV_4,		///< Divider 4 (fCLK/4)
	PMC_DIV_3		///< Divider 3 (fCLK/3)
};

/** 
 * \brief Programmable Clock Controller number enumeration
 */
enum PMC_pck_number {
	PMC_PCK0,		///< PCK number 0
	PMC_PCK1,		///< PCK number 1
	PMC_PCK2,		///< PCK number 2
	PMC_PCK3,		///< PCK number 3
	PMC_PCK4,		///< PCK number 4
	PMC_PCK5,		///< PCK number 5
	PMC_PCK6,		///< PCK number 6
	PMC_PCK7,		///< PCK number 7
};

/**
 * \brief Configure master clock for CPU and peripherals
 * 
 * \param[in] clkSource Clock source for Master clock controller
 * \param[in] pres Prescaler, create clock for CPU
 * \param[in] div Divider, create clock for peripherals
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone Everything OK
 * \retval EInvalidParameter Invalid \c clkSource parameter
 * \retval EDenied PLLA or UPLL clock source isn't stabilized
 */
enum retCode PMC_master_clock_configure(const enum PMC_osc_source clkSource, const enum PMC_prescaler pres, const enum PMC_divider div);

/**
 * \brief Configure and enable Programmable Clock Controller
 * 
 * \param[in] pck PCK number
 * \param[in] clkSource Clock source for Programmable Clock Controller
 * \param[in] pres Prescaler input clock
 *
 * \warning Only three PCK can be used
 */
void PMC_pck_enable(const enum PMC_pck_number pck, const enum PMC_osc_source clkSource, const uint8_t pres);

/**
 * \brief Disable Programmable Clock Controller
 * 
 * \param[in] pck PCK number
 */
static inline void PMC_pck_disable(const enum PMC_pck_number pck)
{
	PMC->PMC_SCDR = (1 << ((uint8_t)pck + 8));
}

/**
 * \brief Enable peripheral clock
 * 
 * \param[in] id Peripheral ID
 */
static inline void PMC_peripheral_clock_enable(const uint8_t id)
{
	PMC->PMC_PCR = (uint32_t)id | PMC_PCR_EN_Msk | PMC_PCR_CMD_Msk;
}

/**
 * \brief Disable peripheral clock
 * 
 * \param[in] id Peripheral ID
 */
static inline void PMC_peripheral_clock_disable(const uint8_t id)
{
	PMC->PMC_PCR = (uint32_t)id | PMC_PCR_CMD_Msk;
}

/**
 * \brief Get actual HCLK frequency
 * 
 * \return \c uint32_t Actual HCLK frequency
 */
uint32_t PMC_get_hclk_freq();

/**
 * \brief Get actual MCK frequency
 * 
 * \return \c uint32_t Actual MCK frequency
 */
uint32_t PMC_get_mck_freq();

/**
 * \brief Get actual PCK frequency
 * 
 * \param[in] pck PCK number
 *
 * \return \c uint32_t Actual PCK frequency
 */
uint32_t PMC_get_pck_freq(const enum PMC_pck_number pck);


#endif /* CLOCKS_H_ */