text: /**
   * \file
   *
   * \brief SAM L21 Generic Clock Driver
   *
   * Copyright (C) 2014-2016 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
   *
   */
  /*
   * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
   */
  
  #include <gclk.h>
  #include <clock.h>
  #include <system_interrupt.h>
  
  
   /**
    * \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 asf_bool_t 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
   */
  asf_bool_t system_gclk_gen_is_enabled(
  		const uint8_t generator)
  {
  	asf_bool_t 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
   */
  asf_bool_t system_gclk_chan_is_enabled(
  		const uint8_t channel)
  {
  	asf_bool_t 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
   */
  asf_bool_t system_gclk_chan_is_locked(
  		const uint8_t channel)
  {
  	asf_bool_t 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);
  }
  