1 /*
2 * Copyright (c) 2016 Piotr Mienkowski
3 * Copyright (c) 2023 Basalte bv
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /** @file
9 * @brief Atmel SAM MCU family Power Management Controller (PMC) module
10 * HAL driver.
11 */
12
13 #ifndef _ATMEL_SAM_SOC_PMC_H_
14 #define _ATMEL_SAM_SOC_PMC_H_
15
16 #include <stdbool.h>
17 #include <zephyr/sys/__assert.h>
18 #include <zephyr/sys/util.h>
19 #include <zephyr/types.h>
20 #include <soc.h>
21
22 /**
23 * @brief Enable the clock of specified peripheral module.
24 *
25 * @param id peripheral module id, as defined in data sheet.
26 */
27 void soc_pmc_peripheral_enable(uint32_t id);
28
29 /**
30 * @brief Disable the clock of specified peripheral module.
31 *
32 * @param id peripheral module id, as defined in data sheet.
33 */
34 void soc_pmc_peripheral_disable(uint32_t id);
35
36 /**
37 * @brief Check if specified peripheral module is enabled.
38 *
39 * @param id peripheral module id, as defined in data sheet.
40 * @return 1 if peripheral is enabled, 0 otherwise
41 */
42 uint32_t soc_pmc_peripheral_is_enabled(uint32_t id);
43
44 #if !defined(CONFIG_SOC_SERIES_SAM4L)
45
46 enum soc_pmc_fast_rc_freq {
47 #if defined(CKGR_MOR_MOSCRCF_4_MHz)
48 SOC_PMC_FAST_RC_FREQ_4MHZ = CKGR_MOR_MOSCRCF_4_MHz,
49 #endif
50 #if defined(CKGR_MOR_MOSCRCF_8_MHz)
51 SOC_PMC_FAST_RC_FREQ_8MHZ = CKGR_MOR_MOSCRCF_8_MHz,
52 #endif
53 #if defined(CKGR_MOR_MOSCRCF_12_MHz)
54 SOC_PMC_FAST_RC_FREQ_12MHZ = CKGR_MOR_MOSCRCF_12_MHz,
55 #endif
56 };
57
58 enum soc_pmc_mck_src {
59 #if defined(PMC_MCKR_CSS_SLOW_CLK)
60 SOC_PMC_MCK_SRC_SLOW_CLK = PMC_MCKR_CSS_SLOW_CLK,
61 #endif
62 #if defined(PMC_MCKR_CSS_MAIN_CLK)
63 SOC_PMC_MCK_SRC_MAIN_CLK = PMC_MCKR_CSS_MAIN_CLK,
64 #endif
65 #if defined(PMC_MCKR_CSS_PLLA_CLK)
66 SOC_PMC_MCK_SRC_PLLA_CLK = PMC_MCKR_CSS_PLLA_CLK,
67 #endif
68 #if defined(PMC_MCKR_CSS_PLLB_CLK)
69 SOC_PMC_MCK_SRC_PLLB_CLK = PMC_MCKR_CSS_PLLB_CLK,
70 #endif
71 #if defined(PMC_MCKR_CSS_UPLL_CLK)
72 SOC_PMC_MCK_SRC_UPLL_CLK = PMC_MCKR_CSS_UPLL_CLK,
73 #endif
74 };
75
76 /**
77 * @brief Set the prescaler of the Master clock.
78 *
79 * @param prescaler the prescaler value.
80 */
soc_pmc_mck_set_prescaler(uint32_t prescaler)81 static ALWAYS_INLINE void soc_pmc_mck_set_prescaler(uint32_t prescaler)
82 {
83 uint32_t reg_val;
84
85 switch (prescaler) {
86 case 1:
87 reg_val = PMC_MCKR_PRES_CLK_1;
88 break;
89 case 2:
90 reg_val = PMC_MCKR_PRES_CLK_2;
91 break;
92 case 4:
93 reg_val = PMC_MCKR_PRES_CLK_4;
94 break;
95 case 8:
96 reg_val = PMC_MCKR_PRES_CLK_8;
97 break;
98 case 16:
99 reg_val = PMC_MCKR_PRES_CLK_16;
100 break;
101 case 32:
102 reg_val = PMC_MCKR_PRES_CLK_32;
103 break;
104 case 64:
105 reg_val = PMC_MCKR_PRES_CLK_64;
106 break;
107 case 3:
108 reg_val = PMC_MCKR_PRES_CLK_3;
109 break;
110 default:
111 __ASSERT(false, "Invalid MCK prescaler");
112 reg_val = PMC_MCKR_PRES_CLK_1;
113 break;
114 }
115
116 PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | reg_val;
117
118 while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) {
119 }
120 }
121
122 #if defined(CONFIG_SOC_SERIES_SAME70) || defined(CONFIG_SOC_SERIES_SAMV71)
123
124 /**
125 * @brief Set the divider of the Master clock.
126 *
127 * @param divider the divider value.
128 */
soc_pmc_mck_set_divider(uint32_t divider)129 static ALWAYS_INLINE void soc_pmc_mck_set_divider(uint32_t divider)
130 {
131 uint32_t reg_val;
132
133 switch (divider) {
134 case 1:
135 reg_val = PMC_MCKR_MDIV_EQ_PCK;
136 break;
137 case 2:
138 reg_val = PMC_MCKR_MDIV_PCK_DIV2;
139 break;
140 case 3:
141 reg_val = PMC_MCKR_MDIV_PCK_DIV3;
142 break;
143 case 4:
144 reg_val = PMC_MCKR_MDIV_PCK_DIV4;
145 break;
146 default:
147 __ASSERT(false, "Invalid MCK divider");
148 reg_val = PMC_MCKR_MDIV_EQ_PCK;
149 break;
150 }
151
152 PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_MDIV_Msk)) | reg_val;
153
154 while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) {
155 }
156 }
157
158 #endif /* CONFIG_SOC_SERIES_SAME70 || CONFIG_SOC_SERIES_SAMV71 */
159
160 /**
161 * @brief Set the source of the Master clock.
162 *
163 * @param source the source identifier.
164 */
soc_pmc_mck_set_source(enum soc_pmc_mck_src source)165 static ALWAYS_INLINE void soc_pmc_mck_set_source(enum soc_pmc_mck_src source)
166 {
167 PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) | (uint32_t)source;
168
169 while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) {
170 }
171 }
172
173 /**
174 * @brief Switch main clock source selection to internal fast RC.
175 *
176 * @param freq the internal fast RC desired frequency 4/8/12MHz.
177 */
soc_pmc_switch_mainck_to_fastrc(enum soc_pmc_fast_rc_freq freq)178 static ALWAYS_INLINE void soc_pmc_switch_mainck_to_fastrc(enum soc_pmc_fast_rc_freq freq)
179 {
180 /* Enable Fast RC oscillator but DO NOT switch to RC now */
181 PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN;
182
183 /* Wait for the Fast RC to stabilize */
184 while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
185 }
186
187 /* Change Fast RC oscillator frequency */
188 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk)
189 | CKGR_MOR_KEY_PASSWD
190 | (uint32_t)freq;
191
192 /* Wait for the Fast RC to stabilize */
193 while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
194 }
195
196 /* Switch to Fast RC */
197 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL)
198 | CKGR_MOR_KEY_PASSWD;
199 }
200
201 /**
202 * @brief Enable internal fast RC oscillator.
203 *
204 * @param freq the internal fast RC desired frequency 4/8/12MHz.
205 */
soc_pmc_osc_enable_fastrc(enum soc_pmc_fast_rc_freq freq)206 static ALWAYS_INLINE void soc_pmc_osc_enable_fastrc(enum soc_pmc_fast_rc_freq freq)
207 {
208 /* Enable Fast RC oscillator but DO NOT switch to RC */
209 PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN;
210
211 /* Wait for the Fast RC to stabilize */
212 while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
213 }
214
215 /* Change Fast RC oscillator frequency */
216 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk)
217 | CKGR_MOR_KEY_PASSWD
218 | (uint32_t)freq;
219
220 /* Wait for the Fast RC to stabilize */
221 while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
222 }
223 }
224
225 /**
226 * @brief Disable internal fast RC oscillator.
227 */
soc_pmc_osc_disable_fastrc(void)228 static ALWAYS_INLINE void soc_pmc_osc_disable_fastrc(void)
229 {
230 /* Disable Fast RC oscillator */
231 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN & ~CKGR_MOR_MOSCRCF_Msk)
232 | CKGR_MOR_KEY_PASSWD;
233 }
234
235 /**
236 * @brief Check if the internal fast RC is ready.
237 *
238 * @return true if internal fast RC is ready, false otherwise
239 */
soc_pmc_osc_is_ready_fastrc(void)240 static ALWAYS_INLINE bool soc_pmc_osc_is_ready_fastrc(void)
241 {
242 return (PMC->PMC_SR & PMC_SR_MOSCRCS);
243 }
244
245 /**
246 * @brief Enable the external crystal oscillator.
247 *
248 * @param xtal_startup_time crystal start-up time, in number of slow clocks.
249 */
soc_pmc_osc_enable_main_xtal(uint32_t xtal_startup_time)250 static ALWAYS_INLINE void soc_pmc_osc_enable_main_xtal(uint32_t xtal_startup_time)
251 {
252 uint32_t mor = PMC->CKGR_MOR;
253
254 mor &= ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN);
255 mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCXTST(xtal_startup_time);
256
257 PMC->CKGR_MOR = mor;
258
259 /* Wait the main Xtal to stabilize */
260 while (!(PMC->PMC_SR & PMC_SR_MOSCXTS)) {
261 }
262 }
263
264 /**
265 * @brief Bypass the external crystal oscillator.
266 */
soc_pmc_osc_bypass_main_xtal(void)267 static ALWAYS_INLINE void soc_pmc_osc_bypass_main_xtal(void)
268 {
269 uint32_t mor = PMC->CKGR_MOR;
270
271 mor &= ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN);
272 mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY;
273
274 /* Enable Crystal oscillator but DO NOT switch now. Keep MOSCSEL to 0 */
275 PMC->CKGR_MOR = mor;
276 /* The MOSCXTS in PMC_SR is automatically set */
277 }
278
279 /**
280 * @brief Disable the external crystal oscillator.
281 */
soc_pmc_osc_disable_main_xtal(void)282 static ALWAYS_INLINE void soc_pmc_osc_disable_main_xtal(void)
283 {
284 uint32_t mor = PMC->CKGR_MOR;
285
286 mor &= ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN);
287
288 PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor;
289 }
290
291 /**
292 * @brief Check if the external crystal oscillator is bypassed.
293 *
294 * @return true if external crystal oscillator is bypassed, false otherwise
295 */
soc_pmc_osc_is_bypassed_main_xtal(void)296 static ALWAYS_INLINE bool soc_pmc_osc_is_bypassed_main_xtal(void)
297 {
298 return (PMC->CKGR_MOR & CKGR_MOR_MOSCXTBY);
299 }
300
301 /**
302 * @brief Check if the external crystal oscillator is ready.
303 *
304 * @return true if external crystal oscillator is ready, false otherwise
305 */
soc_pmc_osc_is_ready_main_xtal(void)306 static ALWAYS_INLINE bool soc_pmc_osc_is_ready_main_xtal(void)
307 {
308 return (PMC->PMC_SR & PMC_SR_MOSCXTS);
309 }
310
311 /**
312 * @brief Switch main clock source selection to external crystal oscillator.
313 *
314 * @param bypass select bypass or xtal
315 * @param xtal_startup_time crystal start-up time, in number of slow clocks
316 */
soc_pmc_switch_mainck_to_xtal(bool bypass,uint32_t xtal_startup_time)317 static ALWAYS_INLINE void soc_pmc_switch_mainck_to_xtal(bool bypass, uint32_t xtal_startup_time)
318 {
319 soc_pmc_osc_enable_main_xtal(xtal_startup_time);
320
321 /* Enable Main Xtal oscillator */
322 if (bypass) {
323 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN)
324 | CKGR_MOR_KEY_PASSWD
325 | CKGR_MOR_MOSCXTBY
326 | CKGR_MOR_MOSCSEL;
327 } else {
328 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY)
329 | CKGR_MOR_KEY_PASSWD
330 | CKGR_MOR_MOSCXTEN
331 | CKGR_MOR_MOSCXTST(xtal_startup_time);
332
333 /* Wait for the Xtal to stabilize */
334 while (!(PMC->PMC_SR & PMC_SR_MOSCXTS)) {
335 }
336
337 PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
338 }
339 }
340
341 /**
342 * @brief Disable the external crystal oscillator.
343 *
344 * @param bypass select bypass or xtal
345 */
soc_pmc_osc_disable_xtal(bool bypass)346 static ALWAYS_INLINE void soc_pmc_osc_disable_xtal(bool bypass)
347 {
348 /* Disable xtal oscillator */
349 if (bypass) {
350 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY)
351 | CKGR_MOR_KEY_PASSWD;
352 } else {
353 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN)
354 | CKGR_MOR_KEY_PASSWD;
355 }
356 }
357
358 /**
359 * @brief Check if the main clock is ready. Depending on MOSCEL, main clock can be one
360 * of external crystal, bypass or internal RC.
361 *
362 * @return true if main clock is ready, false otherwise
363 */
soc_pmc_osc_is_ready_mainck(void)364 static ALWAYS_INLINE bool soc_pmc_osc_is_ready_mainck(void)
365 {
366 return PMC->PMC_SR & PMC_SR_MOSCSELS;
367 }
368
369 /**
370 * @brief Enable Wait Mode.
371 */
soc_pmc_enable_waitmode(void)372 static ALWAYS_INLINE void soc_pmc_enable_waitmode(void)
373 {
374 PMC->PMC_FSMR |= PMC_FSMR_LPM;
375 }
376
377 /**
378 * @brief Enable Clock Failure Detector.
379 */
soc_pmc_enable_clock_failure_detector(void)380 static ALWAYS_INLINE void soc_pmc_enable_clock_failure_detector(void)
381 {
382 uint32_t mor = PMC->CKGR_MOR;
383
384 PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | CKGR_MOR_CFDEN | mor;
385 }
386
387 /**
388 * @brief Disable Clock Failure Detector.
389 */
soc_pmc_disable_clock_failure_detector(void)390 static ALWAYS_INLINE void soc_pmc_disable_clock_failure_detector(void)
391 {
392 uint32_t mor = PMC->CKGR_MOR & (~CKGR_MOR_CFDEN);
393
394 PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor;
395 }
396
397 #if defined(PMC_MCKR_CSS_PLLA_CLK)
398
399 /**
400 * @brief Disable the PLLA clock.
401 */
soc_pmc_disable_pllack(void)402 static ALWAYS_INLINE void soc_pmc_disable_pllack(void)
403 {
404 PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | CKGR_PLLAR_MULA(0);
405 }
406
407 /**
408 * @brief Enable the PLLA clock.
409 *
410 * @param mula PLLA multiplier
411 * @param pllacount PLLA lock counter, in number of slow clocks
412 * @param diva PLLA Divider
413 */
soc_pmc_enable_pllack(uint32_t mula,uint32_t pllacount,uint32_t diva)414 static ALWAYS_INLINE void soc_pmc_enable_pllack(uint32_t mula, uint32_t pllacount, uint32_t diva)
415 {
416 __ASSERT(diva > 0, "Invalid PLLA divider");
417
418 /* first disable the PLL to unlock the lock */
419 soc_pmc_disable_pllack();
420
421 PMC->CKGR_PLLAR = CKGR_PLLAR_ONE
422 | CKGR_PLLAR_DIVA(diva)
423 | CKGR_PLLAR_PLLACOUNT(pllacount)
424 | CKGR_PLLAR_MULA(mula);
425
426 while ((PMC->PMC_SR & PMC_SR_LOCKA) == 0) {
427 }
428 }
429
430 /**
431 * @brief Check if the PLLA is locked.
432 *
433 * @return true if PLLA is locked, false otherwise
434 */
soc_pmc_is_locked_pllack(void)435 static ALWAYS_INLINE bool soc_pmc_is_locked_pllack(void)
436 {
437 return (PMC->PMC_SR & PMC_SR_LOCKA);
438 }
439
440 #endif /* PMC_MCKR_CSS_PLLA_CLK */
441
442 #if defined(PMC_MCKR_CSS_PLLB_CLK)
443
444 /**
445 * @brief Disable the PLLB clock.
446 */
soc_pmc_disable_pllbck(void)447 static ALWAYS_INLINE void soc_pmc_disable_pllbck(void)
448 {
449 PMC->CKGR_PLLBR = CKGR_PLLBR_MULB(0);
450 }
451
452 /**
453 * @brief Enable the PLLB clock.
454 *
455 * @param mulb PLLB multiplier
456 * @param pllbcount PLLB lock counter, in number of slow clocks
457 * @param divb PLLB Divider
458 */
soc_pmc_enable_pllbck(uint32_t mulb,uint32_t pllbcount,uint32_t divb)459 static ALWAYS_INLINE void soc_pmc_enable_pllbck(uint32_t mulb, uint32_t pllbcount, uint32_t divb)
460 {
461 __ASSERT(divb > 0, "Invalid PLLB divider");
462
463 /* first disable the PLL to unlock the lock */
464 soc_pmc_disable_pllbck();
465
466 PMC->CKGR_PLLBR = CKGR_PLLBR_DIVB(divb)
467 | CKGR_PLLBR_PLLBCOUNT(pllbcount)
468 | CKGR_PLLBR_MULB(mulb);
469
470 while ((PMC->PMC_SR & PMC_SR_LOCKB) == 0) {
471 }
472 }
473
474 /**
475 * @brief Check if the PLLB is locked.
476 *
477 * @return true if PLLB is locked, false otherwise
478 */
soc_pmc_is_locked_pllbck(void)479 static ALWAYS_INLINE bool soc_pmc_is_locked_pllbck(void)
480 {
481 return (PMC->PMC_SR & PMC_SR_LOCKB);
482 }
483
484 #endif /* PMC_MCKR_CSS_PLLB_CLK */
485
486 #if defined(PMC_MCKR_CSS_UPLL_CLK)
487
488 /**
489 * @brief Enable the UPLL clock.
490 */
soc_pmc_enable_upllck(uint32_t upllcount)491 static ALWAYS_INLINE void soc_pmc_enable_upllck(uint32_t upllcount)
492 {
493 PMC->CKGR_UCKR = CKGR_UCKR_UPLLCOUNT(upllcount)
494 | CKGR_UCKR_UPLLEN;
495
496 /* Wait UTMI PLL Lock Status */
497 while (!(PMC->PMC_SR & PMC_SR_LOCKU)) {
498 }
499 }
500
501 /**
502 * @brief Disable the UPLL clock.
503 */
soc_pmc_disable_upllck(void)504 static ALWAYS_INLINE void soc_pmc_disable_upllck(void)
505 {
506 PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
507 }
508
509 /**
510 * @brief Check if the UPLL is locked.
511 *
512 * @return true if UPLL is locked, false otherwise
513 */
soc_pmc_is_locked_upllck(void)514 static ALWAYS_INLINE bool soc_pmc_is_locked_upllck(void)
515 {
516 return (PMC->PMC_SR & PMC_SR_LOCKU);
517 }
518
519 #endif /* PMC_MCKR_CSS_UPLL_CLK */
520
521 #endif /* !CONFIG_SOC_SERIES_SAM4L */
522
523 #endif /* _ATMEL_SAM_SOC_PMC_H_ */
524