xref: /btstack/port/samv71-xplained-atwilc3000/ASF/common/services/clock/samv71/pll.h (revision 1b2596b5303dd8caeea8565532c93cca8dab8cc4)
1 /**
2  * \file
3  *
4  * \brief Chip-specific PLL definitions.
5  *
6  * Copyright (c) 2015 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 /*
44  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45  */
46 
47 #ifndef CHIP_PLL_H_INCLUDED
48 #define CHIP_PLL_H_INCLUDED
49 
50 #include <osc.h>
51 
52 /// @cond 0
53 /**INDENT-OFF**/
54 #ifdef __cplusplus
55 extern "C" {
56 #endif
57 /**INDENT-ON**/
58 /// @endcond
59 
60 /**
61  * \weakgroup pll_group
62  * @{
63  */
64 
65 #define PLL_OUTPUT_MIN_HZ       160000000
66 #define PLL_OUTPUT_MAX_HZ       500000000
67 
68 #define PLL_INPUT_MIN_HZ        3000000
69 #define PLL_INPUT_MAX_HZ        32000000
70 
71 #define NR_PLLS             2
72 #define PLLA_ID             0
73 #define UPLL_ID             1   //!< USB UTMI PLL.
74 
75 #define PLL_UPLL_HZ     480000000
76 
77 #define PLL_COUNT   0x3fU
78 
79 enum pll_source {
80 	PLL_SRC_MAINCK_4M_RC   = OSC_MAINCK_4M_RC,  //!< Internal 4MHz RC oscillator.
81 	PLL_SRC_MAINCK_8M_RC   = OSC_MAINCK_8M_RC,  //!< Internal 8MHz RC oscillator.
82 	PLL_SRC_MAINCK_12M_RC  = OSC_MAINCK_12M_RC, //!< Internal 12MHz RC oscillator.
83 	PLL_SRC_MAINCK_XTAL    = OSC_MAINCK_XTAL,   //!< External crystal oscillator.
84 	PLL_SRC_MAINCK_BYPASS  = OSC_MAINCK_BYPASS, //!< External bypass oscillator.
85 	PLL_NR_SOURCES,                             //!< Number of PLL sources.
86 };
87 
88 struct pll_config {
89 	uint32_t ctrl;
90 };
91 
92 #define pll_get_default_rate(pll_id)                                     \
93 	((osc_get_rate(CONFIG_PLL##pll_id##_SOURCE)                      \
94 			* CONFIG_PLL##pll_id##_MUL)                      \
95 			/ CONFIG_PLL##pll_id##_DIV)
96 
97 /* Force UTMI PLL parameters (Hardware defined) */
98 #ifdef CONFIG_PLL1_SOURCE
99 # undef CONFIG_PLL1_SOURCE
100 #endif
101 #ifdef CONFIG_PLL1_MUL
102 # undef CONFIG_PLL1_MUL
103 #endif
104 #ifdef CONFIG_PLL1_DIV
105 # undef CONFIG_PLL1_DIV
106 #endif
107 #define CONFIG_PLL1_SOURCE  PLL_SRC_MAINCK_XTAL
108 #define CONFIG_PLL1_MUL     0
109 #define CONFIG_PLL1_DIV     0
110 
111 /**
112  * \note The SAMV71 PLL hardware interprets mul as mul+1. For readability the
113  * hardware mul+1 is hidden in this implementation. Use mul as mul effective
114  * value.
115  */
pll_config_init(struct pll_config * p_cfg,enum pll_source e_src,uint32_t ul_div,uint32_t ul_mul)116 static inline void pll_config_init(struct pll_config *p_cfg,
117 		enum pll_source e_src, uint32_t ul_div, uint32_t ul_mul)
118 {
119 	uint32_t vco_hz;
120 
121 	Assert(e_src < PLL_NR_SOURCES);
122 
123 	if (ul_div == 0 && ul_mul == 0) { /* Must only be true for UTMI PLL */
124 		p_cfg->ctrl = CKGR_UCKR_UPLLCOUNT(PLL_COUNT);
125 	} else { /* PLLA */
126 	/* Calculate internal VCO frequency */
127 	vco_hz = osc_get_rate(e_src) / ul_div;
128 	Assert(vco_hz >= PLL_INPUT_MIN_HZ);
129 	Assert(vco_hz <= PLL_INPUT_MAX_HZ);
130 
131 	vco_hz *= ul_mul;
132 	Assert(vco_hz >= PLL_OUTPUT_MIN_HZ);
133 	Assert(vco_hz <= PLL_OUTPUT_MAX_HZ);
134 
135 	/* PMC hardware will automatically make it mul+1 */
136 		p_cfg->ctrl = CKGR_PLLAR_MULA(ul_mul - 1) | CKGR_PLLAR_DIVA(ul_div)  \
137 		| CKGR_PLLAR_PLLACOUNT(PLL_COUNT);
138 	}
139 }
140 
141 #define pll_config_defaults(cfg, pll_id)                                 \
142 	pll_config_init(cfg,                                             \
143 			CONFIG_PLL##pll_id##_SOURCE,                     \
144 			CONFIG_PLL##pll_id##_DIV,                        \
145 			CONFIG_PLL##pll_id##_MUL)
146 
pll_config_read(struct pll_config * p_cfg,uint32_t ul_pll_id)147 static inline void pll_config_read(struct pll_config *p_cfg, uint32_t ul_pll_id)
148 {
149 	Assert(ul_pll_id < NR_PLLS);
150 
151 	if (ul_pll_id == PLLA_ID) {
152 		p_cfg->ctrl = PMC->CKGR_PLLAR;
153 	} else {
154 		p_cfg->ctrl = PMC->CKGR_UCKR;
155 	}
156 }
157 
pll_config_write(const struct pll_config * p_cfg,uint32_t ul_pll_id)158 static inline void pll_config_write(const struct pll_config *p_cfg, uint32_t ul_pll_id)
159 {
160 	Assert(ul_pll_id < NR_PLLS);
161 
162 	if (ul_pll_id == PLLA_ID) {
163 		pmc_disable_pllack(); // Always stop PLL first!
164 		PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | p_cfg->ctrl;
165 	} else {
166 		PMC->CKGR_UCKR = p_cfg->ctrl;
167 	}
168 }
169 
pll_enable(const struct pll_config * p_cfg,uint32_t ul_pll_id)170 static inline void pll_enable(const struct pll_config *p_cfg, uint32_t ul_pll_id)
171 {
172 	Assert(ul_pll_id < NR_PLLS);
173 
174 	if (ul_pll_id == PLLA_ID) {
175 		pmc_disable_pllack(); // Always stop PLL first!
176 		PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | p_cfg->ctrl;
177 	} else {
178 		PMC->CKGR_UCKR = p_cfg->ctrl | CKGR_UCKR_UPLLEN;
179 	}
180 }
181 
182 /**
183  * \note This will only disable the selected PLL, not the underlying oscillator (mainck).
184  */
pll_disable(uint32_t ul_pll_id)185 static inline void pll_disable(uint32_t ul_pll_id)
186 {
187 	Assert(ul_pll_id < NR_PLLS);
188 
189 	if (ul_pll_id == PLLA_ID) {
190 		pmc_disable_pllack();
191 	} else {
192 		PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
193 	}
194 }
195 
pll_is_locked(uint32_t ul_pll_id)196 static inline uint32_t pll_is_locked(uint32_t ul_pll_id)
197 {
198 	Assert(ul_pll_id < NR_PLLS);
199 
200 	if (ul_pll_id == PLLA_ID) {
201 	return pmc_is_locked_pllack();
202 	} else {
203 		return pmc_is_locked_upll();
204 	}
205 }
206 
pll_enable_source(enum pll_source e_src)207 static inline void pll_enable_source(enum pll_source e_src)
208 {
209 	switch (e_src) {
210 	case PLL_SRC_MAINCK_4M_RC:
211 	case PLL_SRC_MAINCK_8M_RC:
212 	case PLL_SRC_MAINCK_12M_RC:
213 	case PLL_SRC_MAINCK_XTAL:
214 	case PLL_SRC_MAINCK_BYPASS:
215 		osc_enable(e_src);
216 		osc_wait_ready(e_src);
217 		break;
218 
219 	default:
220 		Assert(false);
221 		break;
222 	}
223 }
224 
pll_enable_config_defaults(unsigned int ul_pll_id)225 static inline void pll_enable_config_defaults(unsigned int ul_pll_id)
226 {
227 	struct pll_config pllcfg;
228 
229 	if (pll_is_locked(ul_pll_id)) {
230 		return; // Pll already running
231 	}
232 	switch (ul_pll_id) {
233 #ifdef CONFIG_PLL0_SOURCE
234 	case 0:
235 		pll_enable_source(CONFIG_PLL0_SOURCE);
236 		pll_config_init(&pllcfg,
237 				CONFIG_PLL0_SOURCE,
238 				CONFIG_PLL0_DIV,
239 				CONFIG_PLL0_MUL);
240 		break;
241 #endif
242 #ifdef CONFIG_PLL1_SOURCE
243 	case 1:
244 		pll_enable_source(CONFIG_PLL1_SOURCE);
245 		pll_config_init(&pllcfg,
246 				CONFIG_PLL1_SOURCE,
247 				CONFIG_PLL1_DIV,
248 				CONFIG_PLL1_MUL);
249 		break;
250 #endif
251 	default:
252 		Assert(false);
253 		break;
254 	}
255 	pll_enable(&pllcfg, ul_pll_id);
256 	while (!pll_is_locked(ul_pll_id));
257 }
258 
259 //! @}
260 
261 /// @cond 0
262 /**INDENT-OFF**/
263 #ifdef __cplusplus
264 }
265 #endif
266 /**INDENT-ON**/
267 /// @endcond
268 
269 #endif /* CHIP_PLL_H_INCLUDED */
270