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