| /** | |
| * \file | |
| * | |
| * \brief SAM D20 Generic Clock Driver | |
| * | |
| * Copyright (C) 2012-2013 Atmel Corporation. All rights reserved. | |
| * | |
| * \asf_license_start | |
| * | |
| * \page License | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright notice, | |
| * this list of conditions and the following disclaimer. | |
| * | |
| * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| * this list of conditions and the following disclaimer in the documentation | |
| * and/or other materials provided with the distribution. | |
| * | |
| * 3. The name of Atmel may not be used to endorse or promote products derived | |
| * from this software without specific prior written permission. | |
| * | |
| * 4. This software may only be redistributed and used in connection with an | |
| * Atmel microcontroller product. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED | |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
| * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR | |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * \asf_license_stop | |
| * | |
| */ | |
| #include <gclk.h> | |
| #include <clock.h> | |
| #include <system_interrupt.h> | |
| /** | |
| * \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, PM_APBAMASK_GCLK); | |
| /* Software reset the module to ensure it is re-initialized correctly */ | |
| GCLK->CTRL.reg = GCLK_CTRL_SWRST; | |
| while (GCLK->CTRL.reg & GCLK_CTRL_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 = (generator << GCLK_GENCTRL_ID_Pos); | |
| uint32_t new_gendiv_config = (generator << GCLK_GENDIV_ID_Pos); | |
| /* 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_gendiv_config |= div2_count << GCLK_GENDIV_DIV_Pos; | |
| new_genctrl_config |= GCLK_GENCTRL_DIVSEL; | |
| } else { | |
| /* Set integer division factor */ | |
| new_gendiv_config |= | |
| (config->division_factor) << GCLK_GENDIV_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()) { | |
| /* Wait for synchronization */ | |
| }; | |
| system_interrupt_enter_critical_section(); | |
| /* Select the correct generator */ | |
| *((uint8_t*)&GCLK->GENDIV.reg) = generator; | |
| /* Write the new generator configuration */ | |
| while (system_gclk_is_syncing()) { | |
| /* Wait for synchronization */ | |
| }; | |
| GCLK->GENDIV.reg = new_gendiv_config; | |
| while (system_gclk_is_syncing()) { | |
| /* Wait for synchronization */ | |
| }; | |
| GCLK->GENCTRL.reg = new_genctrl_config | (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN); | |
| 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()) { | |
| /* Wait for synchronization */ | |
| }; | |
| system_interrupt_enter_critical_section(); | |
| /* Select the requested generator */ | |
| *((uint8_t*)&GCLK->GENCTRL.reg) = generator; | |
| while (system_gclk_is_syncing()) { | |
| /* Wait for synchronization */ | |
| }; | |
| /* Enable generator */ | |
| GCLK->GENCTRL.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()) { | |
| /* Wait for synchronization */ | |
| }; | |
| system_interrupt_enter_critical_section(); | |
| /* Select the requested generator */ | |
| *((uint8_t*)&GCLK->GENCTRL.reg) = generator; | |
| while (system_gclk_is_syncing()) { | |
| /* Wait for synchronization */ | |
| }; | |
| /* Disable generator */ | |
| GCLK->GENCTRL.reg &= ~GCLK_GENCTRL_GENEN; | |
| while (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN) { | |
| /* Wait for clock to become disabled */ | |
| } | |
| system_interrupt_leave_critical_section(); | |
| } | |
| /** | |
| * \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()) { | |
| /* Wait for synchronization */ | |
| }; | |
| system_interrupt_enter_critical_section(); | |
| /* Select the appropriate generator */ | |
| *((uint8_t*)&GCLK->GENCTRL.reg) = generator; | |
| while (system_gclk_is_syncing()) { | |
| /* Wait for synchronization */ | |
| }; | |
| /* 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.bit.SRC); | |
| *((uint8_t*)&GCLK->GENCTRL.reg) = generator; | |
| uint8_t divsel = GCLK->GENCTRL.bit.DIVSEL; | |
| /* Select the appropriate generator division register */ | |
| *((uint8_t*)&GCLK->GENDIV.reg) = generator; | |
| while (system_gclk_is_syncing()) { | |
| /* Wait for synchronization */ | |
| }; | |
| uint32_t divider = GCLK->GENDIV.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); | |
| /* Cache the new config to reduce sync requirements */ | |
| uint32_t new_clkctrl_config = (channel << GCLK_CLKCTRL_ID_Pos); | |
| /* Select the desired generic clock generator */ | |
| new_clkctrl_config |= config->source_generator << GCLK_CLKCTRL_GEN_Pos; | |
| /* Enable write lock if requested to prevent further modification */ | |
| if (config->write_lock) { | |
| new_clkctrl_config |= GCLK_CLKCTRL_WRTLOCK; | |
| } | |
| /* Disable generic clock channel */ | |
| system_gclk_chan_disable(channel); | |
| /* Write the new configuration */ | |
| GCLK->CLKCTRL.reg = new_clkctrl_config; | |
| } | |
| /** | |
| * \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(); | |
| /* Select the requested generator channel */ | |
| *((uint8_t*)&GCLK->CLKCTRL.reg) = channel; | |
| /* Enable the generic clock */ | |
| GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_CLKEN; | |
| 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(); | |
| /* Select the requested generator channel */ | |
| *((uint8_t*)&GCLK->CLKCTRL.reg) = channel; | |
| /* Disable the generic clock */ | |
| GCLK->CLKCTRL.reg &= ~GCLK_CLKCTRL_CLKEN; | |
| while (GCLK->CLKCTRL.reg & GCLK_CLKCTRL_CLKEN) { | |
| /* Wait for clock to become disabled */ | |
| } | |
| system_interrupt_leave_critical_section(); | |
| } | |
| /** | |
| * \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 */ | |
| *((uint8_t*)&GCLK->CLKCTRL.reg) = channel; | |
| gen_id = GCLK->CLKCTRL.bit.GEN; | |
| system_interrupt_leave_critical_section(); | |
| /* Return the clock speed of the associated GCLK generator */ | |
| return system_gclk_gen_get_hz(gen_id); | |
| } |