/** * \file * * \brief Chip-specific PLL definitions. * * Copyright (c) 2013-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 */ #ifndef CHIP_PLL_H_INCLUDED #define CHIP_PLL_H_INCLUDED #include /// @cond 0 /**INDENT-OFF**/ #ifdef __cplusplus extern "C" { #endif /**INDENT-ON**/ /// @endcond /** * \weakgroup pll_group * @{ */ #define PLL_OUTPUT_MIN_HZ 24000000 #if (SAMG51 || SAMG53) #define PLL_OUTPUT_MAX_HZ 48000000 #endif #if (SAMG54) #define PLL_OUTPUT_MAX_HZ 96000000 #endif #if (SAMG55) #define PLL_OUTPUT_MAX_HZ 120000000 #endif #define PLL_INPUT_HZ 32768 #define NR_PLLS 2 #define PLLA_ID 0 #define PLLB_ID 1 #define PLL_COUNT 0x3fU enum pll_source { PLL_SRC_SLCK_RC = OSC_SLCK_32K_RC, //!< Internal 32KHz RC oscillator. PLL_SRC_SLCK_XTAL = OSC_SLCK_32K_XTAL, //!< External 32kHz crystal oscillator. PLL_NR_SOURCES, //!< Number of PLL sources. }; struct pll_config { uint32_t ctrl; }; #define pll_get_default_rate(pll_id) \ ((osc_get_rate(CONFIG_PLL##pll_id##_SOURCE) \ * CONFIG_PLL##pll_id##_MUL) \ / CONFIG_PLL##pll_id##_DIV) /** * \note The SAMG PLL hardware interprets mul as mul+1. For readability the hardware mul+1 * is hidden in this implementation. Use mul as mul effective value. */ static inline void pll_config_init(struct pll_config *p_cfg, enum pll_source e_src, uint32_t ul_div, uint32_t ul_mul) { uint32_t vco_hz; Assert(e_src < PLL_NR_SOURCES); Assert(ul_div < 2); /* Calculate internal VCO frequency */ vco_hz = osc_get_rate(e_src) / ul_div; vco_hz *= ul_mul; Assert(vco_hz >= (PLL_OUTPUT_MIN_HZ - (PLL_OUTPUT_MIN_HZ >> 6))); Assert(vco_hz <= (PLL_OUTPUT_MAX_HZ + (PLL_OUTPUT_MAX_HZ >> 6))); /* PMC hardware will automatically make it mul+1 */ p_cfg->ctrl = CKGR_PLLAR_MULA(ul_mul - 1) | CKGR_PLLAR_PLLAEN(ul_div) | CKGR_PLLAR_PLLACOUNT(PLL_COUNT); } #define pll_config_defaults(cfg, pll_id) \ pll_config_init(cfg, \ CONFIG_PLL##pll_id##_SOURCE, \ CONFIG_PLL##pll_id##_DIV, \ CONFIG_PLL##pll_id##_MUL) static inline void pll_config_read(struct pll_config *p_cfg, uint32_t ul_pll_id) { Assert(ul_pll_id < NR_PLLS); if (ul_pll_id == PLLA_ID) { p_cfg->ctrl = PMC->CKGR_PLLAR; #if SAMG55 } else { p_cfg->ctrl = PMC->CKGR_PLLBR; #endif } } static inline void pll_config_write(const struct pll_config *p_cfg, uint32_t ul_pll_id) { Assert(ul_pll_id < NR_PLLS); if (ul_pll_id == PLLA_ID) { pmc_disable_pllack(); // Always stop PLL first! PMC->CKGR_PLLAR = p_cfg->ctrl; #if SAMG55 } else { pmc_disable_pllbck(); // Always stop PLL first! PMC->CKGR_PLLBR = p_cfg->ctrl; #endif } } static inline void pll_enable(const struct pll_config *p_cfg, uint32_t ul_pll_id) { Assert(ul_pll_id < NR_PLLS); if (ul_pll_id == PLLA_ID) { pmc_disable_pllack(); // Always stop PLL first! PMC->CKGR_PLLAR = p_cfg->ctrl; #if SAMG55 } else { pmc_disable_pllbck(); // Always stop PLL first! PMC->CKGR_PLLBR = p_cfg->ctrl; #endif } } /** * \note This will only disable the selected PLL, not the underlying oscillator (mainck). */ static inline void pll_disable(uint32_t ul_pll_id) { Assert(ul_pll_id < NR_PLLS); if (ul_pll_id == PLLA_ID) { pmc_disable_pllack(); #if SAMG55 } else { pmc_disable_pllbck(); #endif } } static inline uint32_t pll_is_locked(uint32_t ul_pll_id) { Assert(ul_pll_id < NR_PLLS); if (ul_pll_id == PLLA_ID) { return pmc_is_locked_pllack(); #if SAMG55 } else if (ul_pll_id == PLLB_ID) { return pmc_is_locked_pllbck(); #endif } else { return 0; } } static inline void pll_enable_source(enum pll_source e_src) { switch (e_src) { case PLL_SRC_SLCK_RC: case PLL_SRC_SLCK_XTAL: osc_enable(e_src); osc_wait_ready(e_src); break; default: Assert(false); break; } } static inline void pll_enable_config_defaults(unsigned int ul_pll_id) { struct pll_config pllcfg; if (pll_is_locked(ul_pll_id)) { return; // Pll already running } switch (ul_pll_id) { #ifdef CONFIG_PLL0_SOURCE case 0: pll_enable_source(CONFIG_PLL0_SOURCE); pll_config_init(&pllcfg, CONFIG_PLL0_SOURCE, CONFIG_PLL0_DIV, CONFIG_PLL0_MUL); break; #endif #if SAMG55 #ifdef CONFIG_PLL1_SOURCE case 1: pll_enable_source(CONFIG_PLL1_SOURCE); pll_config_init(&pllcfg, CONFIG_PLL1_SOURCE, CONFIG_PLL1_DIV, CONFIG_PLL1_MUL); break; #endif #endif default: Assert(false); break; } pll_enable(&pllcfg, ul_pll_id); while (!pll_is_locked(ul_pll_id)); } //! @} /// @cond 0 /**INDENT-OFF**/ #ifdef __cplusplus } #endif /**INDENT-ON**/ /// @endcond #endif /* CHIP_PLL_H_INCLUDED */