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