aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c')
-rw-r--r--lib/chibios-contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c b/lib/chibios-contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c
new file mode 100644
index 000000000..4cfd1f777
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/ports/NUMICRO/NUC123/hal_lld.c
@@ -0,0 +1,497 @@
1/*
2 Copyright (C) 2020 Alex Lewontin
3 Copyright (C) 2019 /u/KeepItUnder
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16*/
17
18/**
19 * @file hal_lld.c
20 * @brief NUC123 HAL subsystem low level driver source.
21 *
22 * @addtogroup HAL
23 * @{
24 */
25
26#include "hal.h"
27
28/*===========================================================================*/
29/* Driver local definitions. */
30/*===========================================================================*/
31#define FREQ_25MHZ 25000000
32#define FREQ_50MHZ 50000000
33#define FREQ_72MHZ 72000000
34#define FREQ_100MHZ 100000000
35#define FREQ_200MHZ 200000000
36
37#define CLK_CLKDIV_HCLK(x) (((x)-1) << CLK_CLKDIV_HCLK_N_Pos)
38
39/*===========================================================================*/
40/* Driver exported variables. */
41/*===========================================================================*/
42
43/*===========================================================================*/
44/* Driver local variables and types. */
45/*===========================================================================*/
46
47_Bool clock_initialized = FALSE;
48
49uint32_t SystemCoreClock = __HSI; /* System Clock Frequency (Core Clock)*/
50uint32_t CyclesPerUs = (__HSI / 1000000); /* Cycles per micro second */
51uint32_t PllClock = __HSI; /*!< PLL Clock Frequency */
52
53#if (NUC123_CONFIG_ENABLED == TRUE)
54
55static volatile const uint32_t config0 __attribute__((used, unused, section(".nuc123_config0"))) = NUC123_CONFIG0;
56static volatile const uint32_t config1 __attribute__((used, unused, section(".nuc123_config1"))) = NUC123_CONFIG1;
57
58#endif
59
60/*===========================================================================*/
61/* Driver local functions. */
62/*===========================================================================*/
63
64void SystemCoreClockUpdate(void) /* Get Core Clock Frequency */
65{
66 /* ToDo: add code to calculate the system frequency based upon the current
67 register settings.
68 This function can be used to retrieve the system core clock frequeny
69 after user changed register sittings. */
70 /* SystemCoreClock = SYSTEM_CLOCK; */
71
72 uint32_t clkFreq;
73 uint32_t PllReg;
74
75 uint32_t pllFIN, pllNF, pllNR, pllNO;
76
77 /* Update PLL Clock */
78 /* PllClock = clks_lld_get_pll_clock_freq(); */
79 PllReg = CLK->PLLCON;
80
81 if (PllReg & (CLK_PLLCON_PD_Msk | CLK_PLLCON_OE_Msk)) {
82 PllClock = 0; /* PLL is off. */
83 } else {
84
85 if (PllReg & 0x00080000ul) {
86 pllFIN = __HIRC; /* Use HXT for PLL clock */
87 } else {
88 pllFIN = NUC123_HSECLK; /* Use HXT for PLL clock */
89 }
90
91 if (PllReg & CLK_PLLCON_BP_Msk) {
92 PllClock = pllFIN;
93 } else {
94 switch (((PllReg & CLK_PLLCON_OUT_DV_Msk) >> CLK_PLLCON_OUT_DV_Pos)) {
95 case 0: /* OUT_DIV == 00 : NO = 1 */
96 pllNO = 1;
97 break;
98 case 3: /* OUT_DIV == 11 : NO = 4 */
99 pllNO = 4;
100 break;
101 default: /* OUT_DIV == 01 or 10 : NO = 2 */
102 pllNO = 2;
103 break;
104 }
105
106 pllNF = ((PllReg & CLK_PLLCON_FB_DV_Msk) >> CLK_PLLCON_FB_DV_Pos) + 2;
107 pllNR = ((PllReg & CLK_PLLCON_IN_DV_Msk) >> CLK_PLLCON_IN_DV_Pos) + 2;
108
109 /* Shift right to avoid overflow condition */
110 PllClock = (((pllFIN >> 2) * pllNF) / (pllNR * pllNO) << 2);
111 }
112 }
113
114 /* Pick Clock Source */
115 switch (CLK->CLKSEL0 & CLK_CLKSEL0_HCLK_S_Msk) {
116 case 0: /* External HF Xtal */
117 clkFreq = NUC123_HSECLK;
118 break;
119 case 1: /* PLL clock / 2 */
120 clkFreq = PllClock >> 1;
121 break;
122 case 3: /* Internal 10kHz */
123 clkFreq = __LIRC;
124 break;
125 case 2: /* PLL clock */
126 clkFreq = PllClock;
127 break;
128 case 7: /* Internal 22.184MHz */
129 clkFreq = __HIRC;
130 break;
131 default:
132 clkFreq = 0;
133 break;
134 }
135
136 SystemCoreClock = clkFreq / ((CLK->CLKDIV & CLK_CLKDIV_HCLK_N_Msk) + 1);
137 CyclesPerUs = SystemCoreClock / 1000000;
138}
139
140/**
141 * @brief Get PLL clock frequency
142 * @param None
143 * @return PLL frequency
144 * @details This function get PLL frequency. The frequency unit is Hz.
145 */
146static inline uint32_t get_pll_clock_freq(void)
147{
148 uint32_t PllReg;
149 uint32_t pllFIN, pllNF, pllNR, pllNO;
150
151 PllReg = CLK->PLLCON;
152
153 if (PllReg & (CLK_PLLCON_PD_Msk | CLK_PLLCON_OE_Msk)) {
154 PllClock = 0; /* PLL is in power down mode or fix low */
155 } else {
156
157 if (PllReg & NUC123_PLLSRC_HSI) {
158 pllFIN = __HIRC; /* Use HXT for PLL clock */
159 } else {
160 pllFIN = NUC123_HSECLK; /* Use HXT for PLL clock */
161 }
162
163 if (PllReg & CLK_PLLCON_BP_Msk) {
164 PllClock = pllFIN;
165 } else {
166 switch (((PllReg & CLK_PLLCON_OUT_DV_Msk) >> CLK_PLLCON_OUT_DV_Pos)) {
167 case 0: /* OUT_DIV == 00 : NO = 1 */
168 pllNO = 1;
169 break;
170 case 3: /* OUT_DIV == 11 : NO = 4 */
171 pllNO = 4;
172 break;
173 default: /* OUT_DIV == 01 or 10 : NO = 2 */
174 pllNO = 2;
175 break;
176 }
177
178 pllNF = ((PllReg & CLK_PLLCON_FB_DV_Msk) >> CLK_PLLCON_FB_DV_Pos) + 2;
179 pllNR = ((PllReg & CLK_PLLCON_IN_DV_Msk) >> CLK_PLLCON_IN_DV_Pos) + 2;
180
181 /* Shift to avoid overflow condition */
182 PllClock = (((pllFIN >> 2) * pllNF) / (pllNR * pllNO) << 2);
183 }
184 }
185
186 return PllClock;
187}
188
189/**
190 * @brief Wait for stable clock
191 *
192 * @description Always wait around 300ms for clock to be stable
193 *
194 */
195static uint32_t wait_for_clock_ready(uint32_t clkMask)
196{
197 int32_t timeout = 2180000;
198
199 while (timeout-- > 0) {
200 if ((CLK->CLKSTATUS & clkMask) == clkMask) {
201 return 1;
202 }
203 }
204
205 return 0;
206}
207
208/** @brief Set system HCLK
209 *
210 * @description Setup HCLK source and divider
211 *
212 * Always switch to a known stable clock source before changing a
213 * system clock, to avoid issues related to the original clock's
214 * speed/settings.
215 *
216 */
217static void set_HCLK(uint32_t clkSource, uint32_t clkDivider)
218{
219 uint32_t stableHIRC;
220
221 /* Read HIRC clock source stable flag */
222 stableHIRC = CLK->CLKSTATUS & CLK_CLKSTATUS_OSC22M_STB_Msk;
223
224 /* Setup __HIRC */
225 CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
226
227 wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
228
229 /* Use __HIRC as HCLK, temporarily */
230 CLK->CLKSEL0 =
231 (CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLK_S_Msk)) | NUC123_HCLKSRC_HSI;
232
233 /* Set new clock divider */
234 CLK->CLKDIV = (CLK->CLKDIV & (~CLK_CLKDIV_HCLK_N_Msk)) | clkDivider;
235
236 /* Switch HCLK to new HCLK source */
237 CLK->CLKSEL0 = (CLK->CLKSEL0 & (~CLK_CLKSEL0_HCLK_S_Msk)) | clkSource;
238
239 /* Update System Core Clock */
240 SystemCoreClockUpdate();
241
242 /* Disable HIRC if HIRC was disabled before we started */
243 if (stableHIRC == 0) {
244 CLK->PWRCON &= ~CLK_PWRCON_OSC22M_EN_Msk;
245 }
246}
247
248#if NUC123_PLL_ENABLED
249static uint32_t enable_pll(uint32_t pllSrc, uint32_t pllFreq)
250{
251 /* Disable PLL first to avoid unstable when setting PLL. */
252 CLK->PLLCON = CLK_PLLCON_PD_Msk;
253
254 /* Check and setup correct clock source */
255 switch (pllSrc) {
256 case NUC123_PLLSRC_HSE:
257 /* Use HXT clock */
258 CLK->PWRCON |= CLK_PWRCON_XTL12M_EN_Msk;
259
260 /* Wait for stable HXT */
261 wait_for_clock_ready(CLK_CLKSTATUS_XTL12M_STB_Msk);
262
263 break;
264 case NUC123_PLLSRC_HSI:
265 /* Use HIRC clock */
266 CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
267
268 /* Wait for stable HIRC */
269 wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
270
271 break;
272 }
273
274 /**
275 * Calculate best PLL variables from requested frequency
276 *
277 * See NUC123 Technical Reference Manual 5.4.8 PLL Control Register Description, page 124
278 *
279 * NF 1
280 * FOUT = FIN x -- x --
281 * NR NO
282 *
283 */
284
285 uint32_t NO = 0;
286 uint32_t NR = 0;
287 uint32_t clkCalc = 0;
288
289 /* Set "NO" for requested frequency */
290 /* We're using "NO" first to set the PLLCON - so make it "NO" - 1; */
291 if (pllFreq >= FREQ_25MHZ && pllFreq <= FREQ_50MHZ) {
292 /* Low frequency - use full variable headroom */
293 pllFreq <<= 2;
294 NO = 3;
295 } else if (pllFreq > FREQ_50MHZ && pllFreq <= FREQ_100MHZ) {
296 /* Medium frequency - use full variable headroom */
297 pllFreq <<= 1;
298 NO = 1;
299 } else if (pllFreq > FREQ_100MHZ && pllFreq <= FREQ_200MHZ) {
300 /* High frequency - full variable headroom already used */
301 NO = 0;
302 } else {
303 /* Frequency out of range - use default PLL settings
304 *
305 * See NUC123 Technical Reference Manual PLL COntrol Register Description, page 124
306 * The default value: 0xC22E
307 * FIN = 12 MHz
308 * NR = (1+2) = 3
309 * NF = (46+2) = 48
310 * NO = 4
311 * FOUT = 12/4 x 48 x 1/3 = 48 MHz
312 */
313 if (pllSrc == NUC123_PLLSRC_HSE) {
314 CLK->PLLCON = 0xC22E;
315 } else {
316 CLK->PLLCON = 0xD66F;
317 }
318
319 /* Wait for stable PLL clock */
320 wait_for_clock_ready(CLK_CLKSTATUS_PLL_STB_Msk);
321
322 return get_pll_clock_freq();
323 }
324
325 /* Setup "NR" and clkCalc */
326 switch (pllSrc) {
327 case NUC123_PLLSRC_HSE:
328 NR = 2;
329 clkCalc = NUC123_HSECLK;
330 break;
331 case NUC123_PLLSRC_HSI:
332 NR = 4;
333 clkCalc = __HIRC;
334 break;
335 }
336
337 /**
338 * Loop to calculate best/lowest NR (between 0 or 2 and 31) and best/lowest NF (between 0 and 511)
339 *
340 * Best results are off-by-2 until final equation calculation (to allow use in PLLCON)
341 *
342 */
343 uint32_t bestNR = 0;
344 uint32_t bestNF = 0;
345 uint32_t minLimit = -1;
346
347 while (NR <= 33) {
348 uint32_t tmpCalc1 = clkCalc / NR;
349
350 if (tmpCalc1 > 1600000 && tmpCalc1 < 16000000) {
351 uint32_t NF = 2;
352
353 while (NF <= 513) {
354 uint32_t tmpCalc2 = tmpCalc1 * NF;
355
356 if (tmpCalc2 >= 100000000 && tmpCalc2 <= 200000000) {
357 uint32_t tmpCalc3;
358
359 if (tmpCalc2 > pllFreq) {
360 tmpCalc3 = tmpCalc2 - pllFreq;
361 } else {
362 tmpCalc3 = pllFreq - tmpCalc2;
363 }
364
365 if (tmpCalc3 < minLimit) {
366 minLimit = tmpCalc3;
367 bestNF = NF;
368 bestNR = NR;
369
370 /* Stop NF calc loop when minLimit tends back to 0 */
371 if (minLimit == 0)
372 break;
373 }
374 }
375
376 NF++;
377 }
378 }
379
380 NR++;
381 }
382
383 /* Enable and apply new PLL setting. */
384 CLK->PLLCON = pllSrc | (NO << 14) | ((bestNR - 2) << 9) | (bestNF - 2);
385
386 /* Wait for stable PLL clock */
387 wait_for_clock_ready(CLK_CLKSTATUS_PLL_STB_Msk);
388
389 /* Return equation result */
390 return (clkCalc / ((NO + 1) * bestNR) * bestNF);
391}
392
393/** @brief Set Core Clock
394 *
395 * @description Set the core system clock some reference speed (Hz).
396 * This should be between 25MHz and 72MHz for the NUC123SD4AN0.
397 *
398 * Use either the HXT (exact) or HIRC (nearest using 22.1184MHz)
399 * as the clock source.
400 *
401 */
402static uint32_t set_core_clock(uint32_t clkCore)
403{
404 uint32_t stableHIRC;
405
406 /* Read HIRC clock source stable flag */
407 stableHIRC = CLK->CLKSTATUS & CLK_CLKSTATUS_OSC22M_STB_Msk;
408
409 /* Setup __HIRC */
410 CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
411
412 wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
413
414 /* Use __HIRC as HCLK temporarily */
415 CLK->CLKSEL0 |= CLK_CLKSEL0_HCLK_S_Msk;
416 CLK->CLKDIV &= (~CLK_CLKDIV_HCLK_N_Msk);
417
418 /* Is HXT stable ? */
419 if (CLK->CLKSTATUS & CLK_CLKSTATUS_XTL12M_STB_Msk) {
420 /* Use NUC123_HSECLK as PLL source */
421 clkCore = enable_pll(NUC123_PLLSRC_HSE, (2 * clkCore));
422 } else {
423 /* Use __HIRC as PLL source */
424 clkCore = enable_pll(NUC123_PLLSRC_HSI, (2 * clkCore));
425
426 /* Read HIRC clock source stable flag again (since we're using it now) */
427 stableHIRC = CLK->CLKSTATUS & CLK_CLKSTATUS_OSC22M_STB_Msk;
428 }
429
430 /* Set HCLK clock source to PLL */
431 set_HCLK(NUC123_HCLKSRC_PLL_2, CLK_CLKDIV_HCLK(1));
432
433 /* Disable HIRC if HIRC was disabled before we started */
434 if (stableHIRC == 0) {
435 CLK->PWRCON &= ~CLK_PWRCON_OSC22M_EN_Msk;
436 }
437
438 /* Return actual HCLK frequency is PLL frequency divide 2 */
439 return (clkCore >> 1);
440}
441#endif
442
443/*===========================================================================*/
444/* Driver interrupt handlers. */
445/*===========================================================================*/
446
447/*===========================================================================*/
448/* Driver exported functions. */
449/*===========================================================================*/
450
451/**
452 * @brief Low level HAL driver initialization.
453 *
454 * @notapi
455 */
456void hal_lld_init(void)
457{
458 if (!clock_initialized) {
459 NUC123_clock_init();
460 }
461}
462
463void NUC123_clock_init(void)
464{
465 clock_initialized = TRUE;
466 UNLOCKREG();
467
468 /* Always initialize HSI and go from there, things can change later */
469 /* TODO: Technically this could also be the crystal, figure out how to allow
470 * config in linker? */
471 /* Enable HSI */
472 CLK->PWRCON |= CLK_PWRCON_OSC22M_EN_Msk;
473 wait_for_clock_ready(CLK_CLKSTATUS_OSC22M_STB_Msk);
474
475 set_HCLK(NUC123_HCLKSRC_HSI, CLK_CLKDIV_HCLK(1));
476
477#if NUC123_HSE_ENABLED
478 /* SYS->GPF_MFP |= (SYS_GPF_MFP_PF0_XT1_OUT | SYS_GPF_MFP_PF1_XT1_IN); */
479 SYS->GPF_MFP |= (SYS_GPF_MFP_GPF_MFP0_Msk | SYS_GPF_MFP_GPF_MFP1_Msk);
480
481 CLK->PWRCON |= CLK_PWRCON_XTL12M_EN_Msk;
482 wait_for_clock_ready(CLK_CLKSTATUS_XTL12M_STB_Msk);
483#endif /* NUC123_HSE_ENABLED */
484
485#if NUC123_LSI_ENABLED
486 CLK->PWRCON |= CLK_PWRCON_IRC10K_EN_Msk;
487 wait_for_clock_ready(CLK_CLKSTATUS_IRC10K_STB_Msk);
488#endif /* NUC123_LSI_ENABLED */
489
490#if NUC123_PLL_ENABLED
491 set_core_clock(NUC123_HCLK);
492#endif /* NUC123_PLL_ENABLED */
493
494 LOCKREG();
495}
496
497/** @} */