/**
* \file
*
* \brief SAM R30 Generic Clock Driver
*
* Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit Microchip Support
*/
#include
#include
#include
/**
* \brief Determines if the hardware module(s) are currently synchronizing to the bus.
*
* Checks to see if the underlying hardware peripheral module(s) are currently
* synchronizing across multiple clock domains to the hardware bus, This
* function can be used to delay further operations on a module until such time
* that it is ready, to prevent blocking delays for synchronization in the
* user application.
* \param[in] generator Generic Clock Generator index to sync
*
* \return Synchronization status of the underlying hardware module(s).
*
* \retval false if the module has completed synchronization
* \retval true if the module synchronization is ongoing
*/
static inline bool system_gclk_is_syncing(const uint8_t generator)
{
if (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(1 << generator )){
return true;
}
return false;
}
/**
* \brief Initializes the GCLK driver.
*
* Initializes the Generic Clock module, disabling and resetting all active
* Generic Clock Generators and Channels to their power-on default values.
*/
void system_gclk_init(void)
{
/* Turn on the digital interface clock */
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBA, MCLK_APBAMASK_GCLK);
/* Software reset the module to ensure it is re-initialized correctly */
GCLK->CTRLA.reg = GCLK_CTRLA_SWRST;
while (GCLK->CTRLA.reg & GCLK_CTRLA_SWRST) {
/* Wait for reset to complete */
}
}
/**
* \brief Writes a Generic Clock Generator configuration to the hardware module.
*
* Writes out a given configuration of a Generic Clock Generator configuration
* to the hardware module.
*
* \note Changing the clock source on the fly (on a running
* generator) can take additional time if the clock source is configured
* to only run on-demand (ONDEMAND bit is set) and it is not currently
* running (no peripheral is requesting the clock source). In this case
* the GCLK will request the new clock while still keeping a request to
* the old clock source until the new clock source is ready.
*
* \note This function will not start a generator that is not already running;
* to start the generator, call \ref system_gclk_gen_enable()
* after configuring a generator.
*
* \param[in] generator Generic Clock Generator index to configure
* \param[in] config Configuration settings for the generator
*/
void system_gclk_gen_set_config(
const uint8_t generator,
struct system_gclk_gen_config *const config)
{
/* Sanity check arguments */
Assert(config);
/* Cache new register configurations to minimize sync requirements. */
uint32_t new_genctrl_config ;
/* Select the requested source clock for the generator */
new_genctrl_config = config->source_clock << GCLK_GENCTRL_SRC_Pos;
/* Configure the clock to be either high or low when disabled */
if (config->high_when_disabled) {
new_genctrl_config |= GCLK_GENCTRL_OOV;
}
/* Configure if the clock output to I/O pin should be enabled. */
if (config->output_enable) {
new_genctrl_config |= GCLK_GENCTRL_OE;
}
/* Set division factor */
if (config->division_factor > 1) {
/* Check if division is a power of two */
if (((config->division_factor & (config->division_factor - 1)) == 0)) {
/* Determine the index of the highest bit set to get the
* division factor that must be loaded into the division
* register */
uint32_t div2_count = 0;
uint32_t mask;
for (mask = (1UL << 1); mask < config->division_factor;
mask <<= 1) {
div2_count++;
}
/* Set binary divider power of 2 division factor */
new_genctrl_config |= div2_count << GCLK_GENCTRL_DIV_Pos;
new_genctrl_config |= GCLK_GENCTRL_DIVSEL;
} else {
/* Set integer division factor */
new_genctrl_config |=
(config->division_factor) << GCLK_GENCTRL_DIV_Pos;
/* Enable non-binary division with increased duty cycle accuracy */
new_genctrl_config |= GCLK_GENCTRL_IDC;
}
}
/* Enable or disable the clock in standby mode */
if (config->run_in_standby) {
new_genctrl_config |= GCLK_GENCTRL_RUNSTDBY;
}
while (system_gclk_is_syncing(generator)) {
/* Wait for synchronization */
};
system_interrupt_enter_critical_section();
GCLK->GENCTRL[generator].reg = new_genctrl_config | (GCLK->GENCTRL[generator].reg & GCLK_GENCTRL_GENEN);
while (system_gclk_is_syncing(generator)) {
/* Wait for synchronization */
};
system_interrupt_leave_critical_section();
}
/**
* \brief Enables a Generic Clock Generator that was previously configured.
*
* Starts the clock generation of a Generic Clock Generator that was previously
* configured via a call to \ref system_gclk_gen_set_config().
*
* \param[in] generator Generic Clock Generator index to enable
*/
void system_gclk_gen_enable(
const uint8_t generator)
{
while (system_gclk_is_syncing(generator)) {
/* Wait for synchronization */
};
system_interrupt_enter_critical_section();
/* Enable generator */
GCLK->GENCTRL[generator].reg |= GCLK_GENCTRL_GENEN;
system_interrupt_leave_critical_section();
}
/**
* \brief Disables a Generic Clock Generator that was previously enabled.
*
* Stops the clock generation of a Generic Clock Generator that was previously
* started via a call to \ref system_gclk_gen_enable().
*
* \param[in] generator Generic Clock Generator index to disable
*/
void system_gclk_gen_disable(
const uint8_t generator)
{
while (system_gclk_is_syncing(generator)) {
/* Wait for synchronization */
};
system_interrupt_enter_critical_section();
/* Disable generator */
GCLK->GENCTRL[generator].reg &= ~GCLK_GENCTRL_GENEN;
while (GCLK->GENCTRL[generator].reg & GCLK_GENCTRL_GENEN) {
/* Wait for clock to become disabled */
}
system_interrupt_leave_critical_section();
}
/**
* \brief Determins if the specified Generic Clock Generator is enabled.
*
* \param[in] generator Generic Clock Generator index to check
*
* \return The enabled status.
* \retval true The Generic Clock Generator is enabled
* \retval false The Generic Clock Generator is disabled
*/
bool system_gclk_gen_is_enabled(
const uint8_t generator)
{
bool enabled;
system_interrupt_enter_critical_section();
/* Obtain the enabled status */
enabled = (GCLK->GENCTRL[generator].reg & GCLK_GENCTRL_GENEN);
system_interrupt_leave_critical_section();
return enabled;
}
/**
* \brief Retrieves the clock frequency of a Generic Clock generator.
*
* Determines the clock frequency (in Hz) of a specified Generic Clock
* generator, used as a source to a Generic Clock Channel module.
*
* \param[in] generator Generic Clock Generator index
*
* \return The frequency of the generic clock generator, in Hz.
*/
uint32_t system_gclk_gen_get_hz(
const uint8_t generator)
{
while (system_gclk_is_syncing(generator)) {
/* Wait for synchronization */
};
system_interrupt_enter_critical_section();
/* Get the frequency of the source connected to the GCLK generator */
uint32_t gen_input_hz = system_clock_source_get_hz(
(enum system_clock_source)GCLK->GENCTRL[generator].bit.SRC);
uint8_t divsel = GCLK->GENCTRL[generator].bit.DIVSEL;
uint32_t divider = GCLK->GENCTRL[generator].bit.DIV;
system_interrupt_leave_critical_section();
/* Check if the generator is using fractional or binary division */
if (!divsel && divider > 1) {
gen_input_hz /= divider;
} else if (divsel) {
gen_input_hz >>= (divider+1);
}
return gen_input_hz;
}
/**
* \brief Writes a Generic Clock configuration to the hardware module.
*
* Writes out a given configuration of a Generic Clock configuration to the
* hardware module. If the clock is currently running, it will be stopped.
*
* \note Once called the clock will not be running; to start the clock,
* call \ref system_gclk_chan_enable() after configuring a clock channel.
*
* \param[in] channel Generic Clock channel to configure
* \param[in] config Configuration settings for the clock
*
*/
void system_gclk_chan_set_config(
const uint8_t channel,
struct system_gclk_chan_config *const config)
{
/* Sanity check arguments */
Assert(config);
/* Disable generic clock channel */
system_gclk_chan_disable(channel);
/* Configure the peripheral channel */
GCLK->PCHCTRL[channel].reg = GCLK_PCHCTRL_GEN(config->source_generator);
}
/**
* \brief Enables a Generic Clock that was previously configured.
*
* Starts the clock generation of a Generic Clock that was previously
* configured via a call to \ref system_gclk_chan_set_config().
*
* \param[in] channel Generic Clock channel to enable
*/
void system_gclk_chan_enable(
const uint8_t channel)
{
system_interrupt_enter_critical_section();
/* Enable the peripheral channel */
GCLK->PCHCTRL[channel].reg |= GCLK_PCHCTRL_CHEN;
while (!(GCLK->PCHCTRL[channel].reg & GCLK_PCHCTRL_CHEN)) {
/* Wait for clock synchronization */
}
system_interrupt_leave_critical_section();
}
/**
* \brief Disables a Generic Clock that was previously enabled.
*
* Stops the clock generation of a Generic Clock that was previously started
* via a call to \ref system_gclk_chan_enable().
*
* \param[in] channel Generic Clock channel to disable
*/
void system_gclk_chan_disable(
const uint8_t channel)
{
system_interrupt_enter_critical_section();
/* Sanity check WRTLOCK */
Assert(!GCLK->PCHCTRL[channel].bit.WRTLOCK);
/* Disable the peripheral channel */
GCLK->PCHCTRL[channel].reg &= ~GCLK_PCHCTRL_CHEN;
while (GCLK->PCHCTRL[channel].reg & GCLK_PCHCTRL_CHEN) {
/* Wait for clock synchronization */
}
system_interrupt_leave_critical_section();
}
/**
* \brief Determins if the specified Generic Clock channel is enabled.
*
* \param[in] channel Generic Clock Channel index
*
* \return The enabled status.
* \retval true The Generic Clock channel is enabled
* \retval false The Generic Clock channel is disabled
*/
bool system_gclk_chan_is_enabled(
const uint8_t channel)
{
bool enabled;
system_interrupt_enter_critical_section();
/* Select the requested generic clock channel */
enabled = GCLK->PCHCTRL[channel].bit.CHEN;
system_interrupt_leave_critical_section();
return enabled;
}
/**
* \brief Locks a Generic Clock channel from further configuration writes.
*
* Locks a generic clock channel from further configuration writes. It is only
* possible to unlock the channel configuration through a power on reset.
*
* \param[in] channel Generic Clock channel to enable
*/
void system_gclk_chan_lock(
const uint8_t channel)
{
system_interrupt_enter_critical_section();
GCLK->PCHCTRL[channel].reg |= GCLK_PCHCTRL_WRTLOCK | GCLK_PCHCTRL_CHEN;
system_interrupt_leave_critical_section();
}
/**
* \brief Determins if the specified Generic Clock channel is locked.
*
* \param[in] channel Generic Clock Channel index
*
* \return The lock status.
* \retval true The Generic Clock channel is locked
* \retval false The Generic Clock channel is not locked
*/
bool system_gclk_chan_is_locked(
const uint8_t channel)
{
bool locked;
system_interrupt_enter_critical_section();
locked = GCLK->PCHCTRL[channel].bit.WRTLOCK;
system_interrupt_leave_critical_section();
return locked;
}
/**
* \brief Retrieves the clock frequency of a Generic Clock channel.
*
* Determines the clock frequency (in Hz) of a specified Generic Clock
* channel, used as a source to a device peripheral module.
*
* \param[in] channel Generic Clock Channel index
*
* \return The frequency of the generic clock channel, in Hz.
*/
uint32_t system_gclk_chan_get_hz(
const uint8_t channel)
{
uint8_t gen_id;
system_interrupt_enter_critical_section();
/* Select the requested generic clock channel */
gen_id = GCLK->PCHCTRL[channel].bit.GEN;
system_interrupt_leave_critical_section();
/* Return the clock speed of the associated GCLK generator */
return system_gclk_gen_get_hz(gen_id);
}