/** * \file * * \brief SAM R30 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 /** * \internal * \brief DFLL-specific data container. */ struct _system_clock_dfll_config { uint32_t control; uint32_t val; uint32_t mul; }; /** * \internal * \brief DPLL-specific data container. */ struct _system_clock_dpll_config { uint32_t frequency; }; /** * \internal * \brief XOSC-specific data container. */ struct _system_clock_xosc_config { uint32_t frequency; }; /** * \internal * \brief System clock module data container. */ struct _system_clock_module { volatile struct _system_clock_dfll_config dfll; volatile struct _system_clock_dpll_config dpll; volatile struct _system_clock_xosc_config xosc; volatile struct _system_clock_xosc_config xosc32k; }; /** * \internal * \brief Internal module instance to cache configuration values. */ static struct _system_clock_module _system_clock_inst = { .dfll = { .control = 0, .val = 0, .mul = 0, }, .dpll = { .frequency = 0, }, .xosc = { .frequency = 0, }, .xosc32k = { .frequency = 0, }, }; /** * \internal * \brief Wait for sync to the DFLL control registers. */ static inline void _system_dfll_wait_for_sync(void) { while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY)) { /* Wait for DFLL sync */ } } /** * \internal * \brief Wait for sync to the OSC32K control registers. */ static inline void _system_osc32k_wait_for_sync(void) { while (!(OSC32KCTRL->STATUS.reg & OSC32KCTRL_STATUS_OSC32KRDY)) { /* Wait for OSC32K sync */ } } /** * \internal * \brief OSC16M frequency selection. * Frequency selection can be done only when OSC16M is disabled,thus, * OSCULP32K is temporarily used as a new clocksource for mainclock . * */ static inline void _system_clock_source_osc16m_freq_sel(void) { struct system_gclk_gen_config gclk_conf; struct system_clock_source_osc16m_config osc16m_conf; /* Select OSCULP32K as new clock source for mainclock temporarily */ system_gclk_gen_get_config_defaults(&gclk_conf); gclk_conf.source_clock = SYSTEM_CLOCK_SOURCE_ULP32K; system_gclk_gen_set_config(GCLK_GENERATOR_0, &gclk_conf); /* GCLK0 is enabled after POR */ /* Disable OSC16M clock*/ system_clock_source_disable(SYSTEM_CLOCK_SOURCE_OSC16M); /* Switch to new frequency selection and enable OSC16M */ system_clock_source_osc16m_get_config_defaults(&osc16m_conf); osc16m_conf.fsel = CONF_CLOCK_OSC16M_FREQ_SEL; osc16m_conf.on_demand = 0; osc16m_conf.run_in_standby = CONF_CLOCK_OSC16M_RUN_IN_STANDBY; system_clock_source_osc16m_set_config(&osc16m_conf); system_clock_source_enable(SYSTEM_CLOCK_SOURCE_OSC16M); while(!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_OSC16M)); /* Select OSC16M for mainclock again */ system_gclk_gen_get_config_defaults(&gclk_conf); gclk_conf.source_clock = SYSTEM_CLOCK_SOURCE_OSC16M; system_gclk_gen_set_config(GCLK_GENERATOR_0, &gclk_conf); if (CONF_CLOCK_OSC16M_ON_DEMAND){ OSCCTRL->OSC16MCTRL.reg |= OSCCTRL_OSC16MCTRL_ONDEMAND; } } static inline void _system_clock_source_dfll_set_config_errata_9905(void) { /* Disable ONDEMAND mode while writing configurations */ OSCCTRL->DFLLCTRL.reg = OSCCTRL_DFLLCTRL_ENABLE; _system_dfll_wait_for_sync(); OSCCTRL->DFLLMUL.reg = _system_clock_inst.dfll.mul; OSCCTRL->DFLLVAL.reg = _system_clock_inst.dfll.val; /* Write full configuration to DFLL control register */ OSCCTRL->DFLLCTRL.reg = 0; _system_dfll_wait_for_sync(); OSCCTRL->DFLLCTRL.reg = _system_clock_inst.dfll.control; } /** * \brief Retrieve the frequency of a clock source. * * Determines the current operating frequency of a given clock source. * * \param[in] clock_source Clock source * * \returns Frequency of the given clock source, in Hz. */ uint32_t system_clock_source_get_hz( const enum system_clock_source clock_source) { switch (clock_source) { case SYSTEM_CLOCK_SOURCE_XOSC: return _system_clock_inst.xosc.frequency; case SYSTEM_CLOCK_SOURCE_OSC16M: return (OSCCTRL->OSC16MCTRL.bit.FSEL+1)*4000000UL; case SYSTEM_CLOCK_SOURCE_OSC32K: return 32768UL; case SYSTEM_CLOCK_SOURCE_ULP32K: return 32768UL; case SYSTEM_CLOCK_SOURCE_XOSC32K: return _system_clock_inst.xosc32k.frequency; case SYSTEM_CLOCK_SOURCE_DFLL: /* Check if the DFLL has been configured */ if (!(_system_clock_inst.dfll.control & OSCCTRL_DFLLCTRL_ENABLE)) return 0; /* Make sure that the DFLL module is ready */ _system_dfll_wait_for_sync(); /* Check if operating in closed loop (USB) mode */ switch(_system_clock_inst.dfll.control & (OSCCTRL_DFLLCTRL_MODE | OSCCTRL_DFLLCTRL_USBCRM)) { case OSCCTRL_DFLLCTRL_MODE: return system_gclk_chan_get_hz(OSCCTRL_GCLK_ID_DFLL48) * (_system_clock_inst.dfll.mul & 0xffff); default: return 48000000UL; } case SYSTEM_CLOCK_SOURCE_DPLL: if (!(OSCCTRL->DPLLCTRLA.reg & OSCCTRL_DPLLCTRLA_ENABLE)) { return 0; } return _system_clock_inst.dpll.frequency; default: return 0; } } /** * \brief Configure the internal OSC16M oscillator clock source. * * Configures the 16MHz (nominal) internal RC oscillator with the given * configuration settings. * * \note Frequency selection can be done only when OSC16M is disabled. * * \param[in] config OSC16M configuration structure containing the new config */ void system_clock_source_osc16m_set_config( struct system_clock_source_osc16m_config *const config) { OSCCTRL_OSC16MCTRL_Type temp = OSCCTRL->OSC16MCTRL; /* Use temporary struct to reduce register access */ temp.bit.FSEL = config->fsel; temp.bit.ONDEMAND = config->on_demand; temp.bit.RUNSTDBY = config->run_in_standby; OSCCTRL->OSC16MCTRL = temp; } /** * \brief Configure the internal OSC32K oscillator clock source. * * Configures the 32KHz (nominal) internal RC oscillator with the given * configuration settings. * * \param[in] config OSC32K configuration structure containing the new config */ void system_clock_source_osc32k_set_config( struct system_clock_source_osc32k_config *const config) { OSC32KCTRL_OSC32K_Type temp = OSC32KCTRL->OSC32K; /* Update settings via a temporary struct to reduce register access */ temp.bit.EN1K = config->enable_1khz_output; temp.bit.EN32K = config->enable_32khz_output; temp.bit.STARTUP = config->startup_time; temp.bit.ONDEMAND = config->on_demand; temp.bit.RUNSTDBY = config->run_in_standby; temp.bit.WRTLOCK = config->write_once; OSC32KCTRL->OSC32K = temp; } /** * \brief Configure the internal OSCULP32K oscillator clock source. * * Configures the Ultra Low Power 32KHz internal RC oscillator with the given * configuration settings. * * \note The OSCULP32K is enabled by default after a Power On Reset (POR) and * will always run except during POR. * * \param[in] config OSCULP32K configuration structure containing the new config */ void system_clock_source_osculp32k_set_config( struct system_clock_source_osculp32k_config *const config) { OSC32KCTRL_OSCULP32K_Type temp = OSC32KCTRL->OSCULP32K; /* Update settings via a temporary struct to reduce register access */ temp.bit.WRTLOCK = config->write_once; OSC32KCTRL->OSCULP32K = temp; } /** * \brief Configure the external oscillator clock source. * * Configures the external oscillator clock source with the given configuration * settings. * * \param[in] config External oscillator configuration structure containing * the new config */ void system_clock_source_xosc_set_config( struct system_clock_source_xosc_config *const config) { OSCCTRL_XOSCCTRL_Type temp = OSCCTRL->XOSCCTRL; temp.bit.STARTUP = config->startup_time; if (config->external_clock == SYSTEM_CLOCK_EXTERNAL_CRYSTAL) { temp.bit.XTALEN = 1; } else { temp.bit.XTALEN = 0; } temp.bit.AMPGC = config->auto_gain_control; /* Set gain */ if (config->frequency <= 2000000) { temp.bit.GAIN = 0; } else if (config->frequency <= 4000000) { temp.bit.GAIN = 1; } else if (config->frequency <= 8000000) { temp.bit.GAIN = 2; } else if (config->frequency <= 16000000) { temp.bit.GAIN = 3; } else if (config->frequency <= 30000000) { temp.bit.GAIN = 4; } temp.bit.ONDEMAND = config->on_demand; temp.bit.RUNSTDBY = config->run_in_standby; /* Store XOSC frequency for internal use */ _system_clock_inst.xosc.frequency = config->frequency; OSCCTRL->XOSCCTRL = temp; } /** * \brief Configure the XOSC32K external 32KHz oscillator clock source. * * Configures the external 32KHz oscillator clock source with the given * configuration settings. * * \param[in] config XOSC32K configuration structure containing the new config */ void system_clock_source_xosc32k_set_config( struct system_clock_source_xosc32k_config *const config) { OSC32KCTRL_XOSC32K_Type temp = OSC32KCTRL->XOSC32K; temp.bit.STARTUP = config->startup_time; if (config->external_clock == SYSTEM_CLOCK_EXTERNAL_CRYSTAL) { temp.bit.XTALEN = 1; } else { temp.bit.XTALEN = 0; } temp.bit.EN1K = config->enable_1khz_output; temp.bit.EN32K = config->enable_32khz_output; temp.bit.ONDEMAND = config->on_demand; temp.bit.RUNSTDBY = config->run_in_standby; temp.bit.WRTLOCK = config->write_once; /* Cache the new frequency in case the user needs to check the current * operating frequency later */ _system_clock_inst.xosc32k.frequency = config->frequency; OSC32KCTRL->XOSC32K = temp; } /** * \brief Configure the DFLL clock source. * * Configures the Digital Frequency Locked Loop clock source with the given * configuration settings. * * \note The DFLL will be running when this function returns, as the DFLL module * needs to be enabled in order to perform the module configuration. * * \param[in] config DFLL configuration structure containing the new config */ void system_clock_source_dfll_set_config( struct system_clock_source_dfll_config *const config) { _system_clock_inst.dfll.val = OSCCTRL_DFLLVAL_COARSE(config->coarse_value) | OSCCTRL_DFLLVAL_FINE(config->fine_value); _system_clock_inst.dfll.control = (uint32_t)config->wakeup_lock | (uint32_t)config->stable_tracking | (uint32_t)config->quick_lock | (uint32_t)config->chill_cycle | ((uint32_t)config->on_demand << OSCCTRL_DFLLCTRL_ONDEMAND_Pos) | ((uint32_t)config->run_in_stanby << OSCCTRL_DFLLCTRL_RUNSTDBY_Pos); if (config->loop_mode == SYSTEM_CLOCK_DFLL_LOOP_MODE_CLOSED) { _system_clock_inst.dfll.mul = OSCCTRL_DFLLMUL_CSTEP(config->coarse_max_step) | OSCCTRL_DFLLMUL_FSTEP(config->fine_max_step) | OSCCTRL_DFLLMUL_MUL(config->multiply_factor); /* Enable the closed loop mode */ _system_clock_inst.dfll.control |= config->loop_mode; } if (config->loop_mode == SYSTEM_CLOCK_DFLL_LOOP_MODE_USB_RECOVERY) { _system_clock_inst.dfll.mul = OSCCTRL_DFLLMUL_CSTEP(config->coarse_max_step) | OSCCTRL_DFLLMUL_FSTEP(config->fine_max_step) | OSCCTRL_DFLLMUL_MUL(config->multiply_factor); /* Enable the USB recovery mode */ _system_clock_inst.dfll.control |= config->loop_mode | OSCCTRL_DFLLCTRL_MODE | OSCCTRL_DFLLCTRL_BPLCKC; } } /** * \brief Configure the DPLL clock source. * * Configures the Digital Phase-Locked Loop clock source with the given * configuration settings. * * \note The DPLL will be running when this function returns, as the DPLL module * needs to be enabled in order to perform the module configuration. * * \param[in] config DPLL configuration structure containing the new config */ void system_clock_source_dpll_set_config( struct system_clock_source_dpll_config *const config) { uint32_t tmpldr; uint8_t tmpldrfrac; uint32_t refclk; refclk = config->reference_frequency; /* Only reference clock REF1 can be divided */ if (config->reference_clock == SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_XOSC) { refclk = refclk / (2 * (config->reference_divider + 1)); } /* Calculate LDRFRAC and LDR */ tmpldr = (config->output_frequency << 4) / refclk; tmpldrfrac = tmpldr & 0x0f; tmpldr = (tmpldr >> 4) - 1; OSCCTRL->DPLLCTRLA.reg = ((uint32_t)config->on_demand << OSCCTRL_DPLLCTRLA_ONDEMAND_Pos) | ((uint32_t)config->run_in_standby << OSCCTRL_DPLLCTRLA_RUNSTDBY_Pos); OSCCTRL->DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(tmpldrfrac) | OSCCTRL_DPLLRATIO_LDR(tmpldr); while(OSCCTRL->DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO){ } OSCCTRL->DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(config->reference_divider) | ((uint32_t)config->lock_bypass << OSCCTRL_DPLLCTRLB_LBYPASS_Pos) | OSCCTRL_DPLLCTRLB_LTIME(config->lock_time) | OSCCTRL_DPLLCTRLB_REFCLK(config->reference_clock) | ((uint32_t)config->wake_up_fast << OSCCTRL_DPLLCTRLB_WUF_Pos) | ((uint32_t)config->low_power_enable << OSCCTRL_DPLLCTRLB_LPEN_Pos) | OSCCTRL_DPLLCTRLB_FILTER(config->filter); OSCCTRL->DPLLPRESC.reg = OSCCTRL_DPLLPRESC_PRESC(config->prescaler); while(OSCCTRL->DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_DPLLPRESC){ } /* * Fck = Fckrx * (LDR + 1 + LDRFRAC / 16) / (2^PRESC) */ _system_clock_inst.dpll.frequency = (refclk * (((tmpldr + 1) << 4) + tmpldrfrac)) >> (4 + config->prescaler); } /** * \brief Writes the calibration values for a given oscillator clock source. * * Writes an oscillator calibration value to the given oscillator control * registers. The acceptable ranges are: * * For OSC32K: * - 7 bits (max. value 128) * For OSC16MHZ: * - 8 bits (max. value 255) * For OSCULP: * - 5 bits (max. value 32) * * \note The frequency range parameter applies only when configuring the 8MHz * oscillator and will be ignored for the other oscillators. * * \param[in] clock_source Clock source to calibrate * \param[in] calibration_value Calibration value to write * \param[in] freq_range Frequency range (8MHz oscillator only) * * \retval STATUS_OK The calibration value was written * successfully * \retval STATUS_ERR_INVALID_ARG The setting is not valid for selected clock * source */ enum status_code system_clock_source_write_calibration( const enum system_clock_source clock_source, const uint16_t calibration_value, const uint8_t freq_select) { switch (clock_source) { case SYSTEM_CLOCK_SOURCE_OSC16M: //to enable DSU test mode and add calibration value return STATUS_OK; case SYSTEM_CLOCK_SOURCE_OSC32K: if (calibration_value > 128) { return STATUS_ERR_INVALID_ARG; } _system_osc32k_wait_for_sync(); OSC32KCTRL->OSC32K.bit.CALIB = calibration_value; break; case SYSTEM_CLOCK_SOURCE_ULP32K: if (calibration_value > 32) { return STATUS_ERR_INVALID_ARG; } OSC32KCTRL->OSCULP32K.bit.CALIB = calibration_value; break; default: Assert(false); return STATUS_ERR_INVALID_ARG; break; } return STATUS_OK; } /** * \brief Enables a clock source. * * Enables a clock source which has been previously configured. * * \param[in] clock_source Clock source to enable * * \retval STATUS_OK Clock source was enabled successfully and * is ready * \retval STATUS_ERR_INVALID_ARG The clock source is not available on this * device */ enum status_code system_clock_source_enable( const enum system_clock_source clock_source) { switch (clock_source) { case SYSTEM_CLOCK_SOURCE_OSC16M: OSCCTRL->OSC16MCTRL.reg |= OSCCTRL_OSC16MCTRL_ENABLE; return STATUS_OK; case SYSTEM_CLOCK_SOURCE_OSC32K: OSC32KCTRL->OSC32K.reg |= OSC32KCTRL_OSC32K_ENABLE; break; case SYSTEM_CLOCK_SOURCE_XOSC: OSCCTRL->XOSCCTRL.reg |= OSCCTRL_XOSCCTRL_ENABLE; break; case SYSTEM_CLOCK_SOURCE_XOSC32K: OSC32KCTRL->XOSC32K.reg |= OSC32KCTRL_XOSC32K_ENABLE; break; case SYSTEM_CLOCK_SOURCE_DFLL: _system_clock_inst.dfll.control |= OSCCTRL_DFLLCTRL_ENABLE; _system_clock_source_dfll_set_config_errata_9905(); break; case SYSTEM_CLOCK_SOURCE_DPLL: OSCCTRL->DPLLCTRLA.reg |= OSCCTRL_DPLLCTRLA_ENABLE; while(OSCCTRL->DPLLSYNCBUSY.reg & OSCCTRL_DPLLSYNCBUSY_ENABLE){ } break; case SYSTEM_CLOCK_SOURCE_ULP32K: /* Always enabled */ return STATUS_OK; default: Assert(false); return STATUS_ERR_INVALID_ARG; } return STATUS_OK; } /** * \brief Disables a clock source. * * Disables a clock source that was previously enabled. * * \param[in] clock_source Clock source to disable * * \retval STATUS_OK Clock source was disabled successfully * \retval STATUS_ERR_INVALID_ARG An invalid or unavailable clock source was * given */ enum status_code system_clock_source_disable( const enum system_clock_source clock_source) { switch (clock_source) { case SYSTEM_CLOCK_SOURCE_OSC16M: OSCCTRL->OSC16MCTRL.reg &= ~OSCCTRL_OSC16MCTRL_ENABLE; break; case SYSTEM_CLOCK_SOURCE_OSC32K: OSC32KCTRL->OSC32K.reg &= ~OSC32KCTRL_OSC32K_ENABLE; break; case SYSTEM_CLOCK_SOURCE_XOSC: OSCCTRL->XOSCCTRL.reg &= ~OSCCTRL_XOSCCTRL_ENABLE; break; case SYSTEM_CLOCK_SOURCE_XOSC32K: OSC32KCTRL->XOSC32K.reg &= ~OSC32KCTRL_XOSC32K_ENABLE; break; case SYSTEM_CLOCK_SOURCE_DFLL: _system_clock_inst.dfll.control &= ~OSCCTRL_DFLLCTRL_ENABLE; OSCCTRL->DFLLCTRL.reg = _system_clock_inst.dfll.control; break; case SYSTEM_CLOCK_SOURCE_DPLL: OSCCTRL->DPLLCTRLA.reg &= ~OSCCTRL_DPLLCTRLA_ENABLE; break; case SYSTEM_CLOCK_SOURCE_ULP32K: /* Not possible to disable */ default: Assert(false); return STATUS_ERR_INVALID_ARG; } return STATUS_OK; } /** * \brief Checks if a clock source is ready. * * Checks if a given clock source is ready to be used. * * \param[in] clock_source Clock source to check if ready * * \returns Ready state of the given clock source. * * \retval true Clock source is enabled and ready * \retval false Clock source is disabled or not yet ready */ bool system_clock_source_is_ready( const enum system_clock_source clock_source) { uint32_t mask = 0; switch (clock_source) { case SYSTEM_CLOCK_SOURCE_OSC16M: mask = OSCCTRL_STATUS_OSC16MRDY; return ((OSCCTRL->STATUS.reg & mask) == mask); case SYSTEM_CLOCK_SOURCE_OSC32K: mask = OSC32KCTRL_STATUS_OSC32KRDY; return ((OSC32KCTRL->STATUS.reg & mask) == mask); case SYSTEM_CLOCK_SOURCE_XOSC: mask = OSCCTRL_STATUS_XOSCRDY; return ((OSCCTRL->STATUS.reg & mask) == mask); case SYSTEM_CLOCK_SOURCE_XOSC32K: mask = OSC32KCTRL_STATUS_XOSC32KRDY; return ((OSC32KCTRL->STATUS.reg & mask) == mask); case SYSTEM_CLOCK_SOURCE_DFLL: if (CONF_CLOCK_DFLL_LOOP_MODE == SYSTEM_CLOCK_DFLL_LOOP_MODE_CLOSED) { mask = (OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKF | OSCCTRL_STATUS_DFLLLCKC); } else { mask = OSCCTRL_STATUS_DFLLRDY; } return ((OSCCTRL->STATUS.reg & mask) == mask); case SYSTEM_CLOCK_SOURCE_DPLL: return ((OSCCTRL->DPLLSTATUS.reg & (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK)) == (OSCCTRL_DPLLSTATUS_CLKRDY | OSCCTRL_DPLLSTATUS_LOCK)); case SYSTEM_CLOCK_SOURCE_ULP32K: /* Not possible to disable */ return true; default: return false; } } /* Include some checks for conf_clocks.h validation */ #include "clock_config_check.h" #if !defined(__DOXYGEN__) /** \internal * * Configures a Generic Clock Generator with the configuration from \c conf_clocks.h. */ # define _CONF_CLOCK_GCLK_CONFIG(n, unused) \ if (CONF_CLOCK_GCLK_##n##_ENABLE == true) { \ struct system_gclk_gen_config gclk_conf; \ system_gclk_gen_get_config_defaults(&gclk_conf); \ gclk_conf.source_clock = CONF_CLOCK_GCLK_##n##_CLOCK_SOURCE; \ gclk_conf.division_factor = CONF_CLOCK_GCLK_##n##_PRESCALER; \ gclk_conf.run_in_standby = CONF_CLOCK_GCLK_##n##_RUN_IN_STANDBY; \ gclk_conf.output_enable = CONF_CLOCK_GCLK_##n##_OUTPUT_ENABLE; \ system_gclk_gen_set_config(GCLK_GENERATOR_##n, &gclk_conf); \ system_gclk_gen_enable(GCLK_GENERATOR_##n); \ } /** \internal * * Configures a Generic Clock Generator with the configuration from \c conf_clocks.h, * provided that it is not the main Generic Clock Generator channel. */ # define _CONF_CLOCK_GCLK_CONFIG_NONMAIN(n, unused) \ if (n > 0) { _CONF_CLOCK_GCLK_CONFIG(n, unused); } #endif /** * \brief Initialize clock system based on the configuration in conf_clocks.h. * * This function will apply the settings in conf_clocks.h when run from the user * application. All clock sources and GCLK generators are running when this function * returns. * * \note OSC16M is always enabled and if user selects other clocks for GCLK generators, * the OSC16M default enable can be disabled after system_clock_init. Make sure the * clock switches successfully before disabling OSC8M. */ void system_clock_init(void) { /* Various bits in the INTFLAG register can be set to one at startup. This will ensure that these bits are cleared */ OSCCTRL->INTFLAG.reg = OSCCTRL_INTFLAG_DFLLRDY; SUPC->INTFLAG.reg = SUPC_INTFLAG_BOD33RDY | SUPC_INTFLAG_BOD33DET; system_flash_set_waitstates(CONF_CLOCK_FLASH_WAIT_STATES); /* Switch to PL2 to be sure configuration of GCLK0 is safe */ system_switch_performance_level(SYSTEM_PERFORMANCE_LEVEL_2); /* XOSC */ #if CONF_CLOCK_XOSC_ENABLE == true struct system_clock_source_xosc_config xosc_conf; system_clock_source_xosc_get_config_defaults(&xosc_conf); xosc_conf.external_clock = CONF_CLOCK_XOSC_EXTERNAL_CRYSTAL; xosc_conf.startup_time = CONF_CLOCK_XOSC_STARTUP_TIME; xosc_conf.frequency = CONF_CLOCK_XOSC_EXTERNAL_FREQUENCY; xosc_conf.run_in_standby = CONF_CLOCK_XOSC_RUN_IN_STANDBY; system_clock_source_xosc_set_config(&xosc_conf); system_clock_source_enable(SYSTEM_CLOCK_SOURCE_XOSC); while(!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_XOSC)); if (CONF_CLOCK_XOSC_ON_DEMAND || CONF_CLOCK_XOSC_AUTO_GAIN_CONTROL) { OSCCTRL->XOSCCTRL.reg |= (CONF_CLOCK_XOSC_ON_DEMAND << OSCCTRL_XOSCCTRL_ONDEMAND_Pos) | (CONF_CLOCK_XOSC_AUTO_GAIN_CONTROL << OSCCTRL_XOSCCTRL_AMPGC_Pos); } #endif /* XOSC32K */ #if CONF_CLOCK_XOSC32K_ENABLE == true struct system_clock_source_xosc32k_config xosc32k_conf; system_clock_source_xosc32k_get_config_defaults(&xosc32k_conf); xosc32k_conf.frequency = 32768UL; xosc32k_conf.external_clock = CONF_CLOCK_XOSC32K_EXTERNAL_CRYSTAL; xosc32k_conf.startup_time = CONF_CLOCK_XOSC32K_STARTUP_TIME; xosc32k_conf.enable_1khz_output = CONF_CLOCK_XOSC32K_ENABLE_1KHZ_OUPUT; xosc32k_conf.enable_32khz_output = CONF_CLOCK_XOSC32K_ENABLE_32KHZ_OUTPUT; xosc32k_conf.on_demand = false; xosc32k_conf.run_in_standby = CONF_CLOCK_XOSC32K_RUN_IN_STANDBY; system_clock_source_xosc32k_set_config(&xosc32k_conf); system_clock_source_enable(SYSTEM_CLOCK_SOURCE_XOSC32K); while(!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_XOSC32K)); if (CONF_CLOCK_XOSC32K_ON_DEMAND) { OSC32KCTRL->XOSC32K.bit.ONDEMAND = 1; } #endif /* OSCK32K */ #if CONF_CLOCK_OSC32K_ENABLE == true struct system_clock_source_osc32k_config osc32k_conf; system_clock_source_osc32k_get_config_defaults(&osc32k_conf); osc32k_conf.startup_time = CONF_CLOCK_OSC32K_STARTUP_TIME; osc32k_conf.enable_1khz_output = CONF_CLOCK_OSC32K_ENABLE_1KHZ_OUTPUT; osc32k_conf.enable_32khz_output = CONF_CLOCK_OSC32K_ENABLE_32KHZ_OUTPUT; osc32k_conf.on_demand = CONF_CLOCK_OSC32K_ON_DEMAND; osc32k_conf.run_in_standby = CONF_CLOCK_OSC32K_RUN_IN_STANDBY; system_clock_source_osc32k_set_config(&osc32k_conf); system_clock_source_enable(SYSTEM_CLOCK_SOURCE_OSC32K); #endif /* OSC16M */ if (CONF_CLOCK_OSC16M_FREQ_SEL == SYSTEM_OSC16M_4M){ OSCCTRL->OSC16MCTRL.bit.ONDEMAND = CONF_CLOCK_OSC16M_ON_DEMAND ; OSCCTRL->OSC16MCTRL.bit.RUNSTDBY = CONF_CLOCK_OSC16M_RUN_IN_STANDBY; } else { _system_clock_source_osc16m_freq_sel(); } /* DFLL Config (Open and Closed Loop) */ #if CONF_CLOCK_DFLL_ENABLE == true struct system_clock_source_dfll_config dfll_conf; system_clock_source_dfll_get_config_defaults(&dfll_conf); dfll_conf.loop_mode = CONF_CLOCK_DFLL_LOOP_MODE; dfll_conf.on_demand = false; dfll_conf.run_in_stanby = CONF_CLOCK_DFLL_RUN_IN_STANDBY; /* Using DFLL48M COARSE CAL value from NVM Software Calibration Area Mapping in DFLL.COARSE helps to output a frequency close to 48 MHz.*/ #define NVM_DFLL_COARSE_POS 26 /* DFLL48M Coarse calibration value bit position.*/ #define NVM_DFLL_COARSE_SIZE 6 /* DFLL48M Coarse calibration value bit size.*/ uint32_t coarse =( *((uint32_t *)(NVMCTRL_OTP5) + (NVM_DFLL_COARSE_POS / 32)) >> (NVM_DFLL_COARSE_POS % 32)) & ((1 << NVM_DFLL_COARSE_SIZE) - 1); /* In some revision chip, the Calibration value is not correct */ if (coarse == 0x3f) { coarse = 0x1f; } dfll_conf.coarse_value = coarse; if (CONF_CLOCK_DFLL_LOOP_MODE == SYSTEM_CLOCK_DFLL_LOOP_MODE_OPEN) { dfll_conf.fine_value = CONF_CLOCK_DFLL_FINE_VALUE; } # if CONF_CLOCK_DFLL_QUICK_LOCK == true dfll_conf.quick_lock = SYSTEM_CLOCK_DFLL_QUICK_LOCK_ENABLE; # else dfll_conf.quick_lock = SYSTEM_CLOCK_DFLL_QUICK_LOCK_DISABLE; # endif # if CONF_CLOCK_DFLL_TRACK_AFTER_FINE_LOCK == true dfll_conf.stable_tracking = SYSTEM_CLOCK_DFLL_STABLE_TRACKING_TRACK_AFTER_LOCK; # else dfll_conf.stable_tracking = SYSTEM_CLOCK_DFLL_STABLE_TRACKING_FIX_AFTER_LOCK; # endif # if CONF_CLOCK_DFLL_KEEP_LOCK_ON_WAKEUP == true dfll_conf.wakeup_lock = SYSTEM_CLOCK_DFLL_WAKEUP_LOCK_KEEP; # else dfll_conf.wakeup_lock = SYSTEM_CLOCK_DFLL_WAKEUP_LOCK_LOSE; # endif # if CONF_CLOCK_DFLL_ENABLE_CHILL_CYCLE == true dfll_conf.chill_cycle = SYSTEM_CLOCK_DFLL_CHILL_CYCLE_ENABLE; # else dfll_conf.chill_cycle = SYSTEM_CLOCK_DFLL_CHILL_CYCLE_DISABLE; # endif if (CONF_CLOCK_DFLL_LOOP_MODE == SYSTEM_CLOCK_DFLL_LOOP_MODE_CLOSED) { dfll_conf.multiply_factor = CONF_CLOCK_DFLL_MULTIPLY_FACTOR; } dfll_conf.coarse_max_step = CONF_CLOCK_DFLL_MAX_COARSE_STEP_SIZE; dfll_conf.fine_max_step = CONF_CLOCK_DFLL_MAX_FINE_STEP_SIZE; if (CONF_CLOCK_DFLL_LOOP_MODE == SYSTEM_CLOCK_DFLL_LOOP_MODE_USB_RECOVERY) { dfll_conf.fine_max_step = 10; dfll_conf.fine_value = 0x1ff; dfll_conf.quick_lock = SYSTEM_CLOCK_DFLL_QUICK_LOCK_ENABLE; dfll_conf.stable_tracking = SYSTEM_CLOCK_DFLL_STABLE_TRACKING_TRACK_AFTER_LOCK; dfll_conf.wakeup_lock = SYSTEM_CLOCK_DFLL_WAKEUP_LOCK_KEEP; dfll_conf.chill_cycle = SYSTEM_CLOCK_DFLL_CHILL_CYCLE_DISABLE; dfll_conf.multiply_factor = 48000; } system_clock_source_dfll_set_config(&dfll_conf); #endif /* GCLK */ #if CONF_CLOCK_CONFIGURE_GCLK == true system_gclk_init(); /* Configure all GCLK generators except for the main generator, which * is configured later after all other clock systems are set up */ MREPEAT(GCLK_GEN_NUM, _CONF_CLOCK_GCLK_CONFIG_NONMAIN, ~); # if CONF_CLOCK_DFLL_ENABLE == true /* Enable DFLL reference clock if in closed loop mode */ if (CONF_CLOCK_DFLL_LOOP_MODE == SYSTEM_CLOCK_DFLL_LOOP_MODE_CLOSED) { struct system_gclk_chan_config dfll_gclk_chan_conf; system_gclk_chan_get_config_defaults(&dfll_gclk_chan_conf); dfll_gclk_chan_conf.source_generator = CONF_CLOCK_DFLL_SOURCE_GCLK_GENERATOR; system_gclk_chan_set_config(OSCCTRL_GCLK_ID_DFLL48, &dfll_gclk_chan_conf); system_gclk_chan_enable(OSCCTRL_GCLK_ID_DFLL48); } # endif # if CONF_CLOCK_DPLL_ENABLE == true /* Enable DPLL internal lock timer and reference clock */ struct system_gclk_chan_config dpll_gclk_chan_conf; system_gclk_chan_get_config_defaults(&dpll_gclk_chan_conf); if (CONF_CLOCK_DPLL_LOCK_TIME != SYSTEM_CLOCK_SOURCE_DPLL_LOCK_TIME_DEFAULT) { dpll_gclk_chan_conf.source_generator = CONF_CLOCK_DPLL_LOCK_GCLK_GENERATOR; system_gclk_chan_set_config(OSCCTRL_GCLK_ID_FDPLL32K, &dpll_gclk_chan_conf); system_gclk_chan_enable(OSCCTRL_GCLK_ID_FDPLL32K); } if (CONF_CLOCK_DPLL_REFERENCE_CLOCK == SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_GCLK) { dpll_gclk_chan_conf.source_generator = CONF_CLOCK_DPLL_REFERENCE_GCLK_GENERATOR; system_gclk_chan_set_config(OSCCTRL_GCLK_ID_FDPLL, &dpll_gclk_chan_conf); system_gclk_chan_enable(OSCCTRL_GCLK_ID_FDPLL); } # endif #endif /* DFLL Enable (Open and Closed Loop) */ #if CONF_CLOCK_DFLL_ENABLE == true system_clock_source_enable(SYSTEM_CLOCK_SOURCE_DFLL); while(!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_DFLL)); if (CONF_CLOCK_DFLL_ON_DEMAND) { OSCCTRL->DFLLCTRL.bit.ONDEMAND = 1; } #endif /* DPLL */ # if (CONF_CLOCK_DPLL_ENABLE == true) /* Enable DPLL reference clock */ if (CONF_CLOCK_DPLL_REFERENCE_CLOCK == SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_XOSC32K) { /* XOSC32K should have been enabled for GCLK_XOSC32 */ Assert(CONF_CLOCK_XOSC32K_ENABLE); } else if (CONF_CLOCK_DPLL_REFERENCE_CLOCK == SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_XOSC) { /* XOSC should have been enabled for GCLK_XOSC */ Assert(CONF_CLOCK_XOSC_ENABLE); } else if (CONF_CLOCK_DPLL_REFERENCE_CLOCK == SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_GCLK) { /* GCLK should have been enabled */ Assert(CONF_CLOCK_CONFIGURE_GCLK); } else { Assert(false); } struct system_clock_source_dpll_config dpll_config; system_clock_source_dpll_get_config_defaults(&dpll_config); dpll_config.on_demand = false; dpll_config.run_in_standby = CONF_CLOCK_DPLL_RUN_IN_STANDBY; dpll_config.lock_bypass = CONF_CLOCK_DPLL_LOCK_BYPASS; dpll_config.wake_up_fast = CONF_CLOCK_DPLL_WAKE_UP_FAST; dpll_config.low_power_enable = CONF_CLOCK_DPLL_LOW_POWER_ENABLE; dpll_config.filter = CONF_CLOCK_DPLL_FILTER; dpll_config.lock_time = CONF_CLOCK_DPLL_LOCK_TIME; dpll_config.reference_clock = CONF_CLOCK_DPLL_REFERENCE_CLOCK; dpll_config.reference_frequency = CONF_CLOCK_DPLL_REFERENCE_FREQUENCY; dpll_config.reference_divider = CONF_CLOCK_DPLL_REFERENCE_DIVIDER; dpll_config.output_frequency = CONF_CLOCK_DPLL_OUTPUT_FREQUENCY; dpll_config.prescaler = CONF_CLOCK_DPLL_PRESCALER; system_clock_source_dpll_set_config(&dpll_config); system_clock_source_enable(SYSTEM_CLOCK_SOURCE_DPLL); while(!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_DPLL)); if (CONF_CLOCK_DPLL_ON_DEMAND) { OSCCTRL->DPLLCTRLA.bit.ONDEMAND = 1; } # endif /* CPU and BUS clocks */ system_backup_clock_set_divider(CONF_CLOCK_BACKUP_DIVIDER); system_low_power_clock_set_divider(CONF_CLOCK_LOW_POWER_DIVIDER); system_cpu_clock_set_divider(CONF_CLOCK_CPU_DIVIDER); system_main_clock_set_failure_detect(CONF_CLOCK_CPU_CLOCK_FAILURE_DETECT); /* GCLK 0 */ #if CONF_CLOCK_CONFIGURE_GCLK == true /* Configure the main GCLK last as it might depend on other generators */ _CONF_CLOCK_GCLK_CONFIG(0, ~); #endif /* If CPU frequency is less than 12MHz, scale down performance level to PL0 */ uint32_t cpu_freq = system_cpu_clock_get_hz(); if (cpu_freq <= 12000000) { system_switch_performance_level(SYSTEM_PERFORMANCE_LEVEL_0); } }