1 /*
2  * Copyright 2024 NXP
3  * Copyright (c) 2019-2021 Vestas Wind Systems A/S
4  *
5  * Based on NXP k6x soc.c, which is:
6  * Copyright (c) 2014-2015 Wind River Systems, Inc.
7  * Copyright (c) 2016, Freescale Semiconductor, Inc.
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/device.h>
14 #include <zephyr/init.h>
15 #include <fsl_clock.h>
16 #include <cmsis_core.h>
17 
18 #define ASSERT_WITHIN_RANGE(val, min, max, str) \
19 	BUILD_ASSERT(val >= min && val <= max, str)
20 
21 #define ASSERT_ASYNC_CLK_DIV_VALID(val, str) \
22 	BUILD_ASSERT(val == 0 || val == 1 || val == 2 || val == 4 ||	\
23 		     val == 8 || val == 16 || val == 2 || val == 64, str)
24 
25 #define TO_SYS_CLK_DIV(val) _DO_CONCAT(kSCG_SysClkDivBy, val)
26 
27 #define kSCG_AsyncClkDivBy0 kSCG_AsyncClkDisable
28 #define TO_ASYNC_CLK_DIV(val) _DO_CONCAT(kSCG_AsyncClkDivBy, val)
29 
30 #define SCG_CLOCK_NODE(name) DT_CHILD(DT_INST(0, nxp_kinetis_scg), name)
31 #define SCG_CLOCK_DIV(name) DT_PROP(SCG_CLOCK_NODE(name), clock_div)
32 
33 /* System Clock configuration */
34 ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(bus_clk), 2, 8,
35 		    "Invalid SCG bus clock divider value");
36 ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(core_clk), 1, 16,
37 		    "Invalid SCG core clock divider value");
38 
39 static const scg_sys_clk_config_t scg_sys_clk_config = {
40 	.divSlow = TO_SYS_CLK_DIV(SCG_CLOCK_DIV(bus_clk)),
41 	.divCore = TO_SYS_CLK_DIV(SCG_CLOCK_DIV(core_clk)),
42 #if DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(sirc_clk))
43 	.src     = kSCG_SysClkSrcSirc,
44 #elif DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(firc_clk))
45 	.src     = kSCG_SysClkSrcFirc,
46 #endif
47 };
48 
49 /* Slow Internal Reference Clock (SIRC) configuration */
50 ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(sircdiv2_clk),
51 		       "Invalid SCG SIRC divider 2 value");
52 static const scg_sirc_config_t scg_sirc_config = {
53 	.enableMode = kSCG_SircEnable,
54 	.div2       = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(sircdiv2_clk)),
55 #if MHZ(2) == DT_PROP(SCG_CLOCK_NODE(sirc_clk), clock_frequency)
56 	.range      = kSCG_SircRangeLow
57 #elif MHZ(8) == DT_PROP(SCG_CLOCK_NODE(sirc_clk), clock_frequency)
58 	.range      = kSCG_SircRangeHigh
59 #else
60 #error Invalid SCG SIRC clock frequency
61 #endif
62 };
63 
64 /* Fast Internal Reference Clock (FIRC) configuration */
65 ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(fircdiv2_clk),
66 		       "Invalid SCG FIRC divider 2 value");
67 static const scg_firc_config_t scg_firc_config = {
68 	.enableMode = kSCG_FircEnable,
69 	.div2       = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(fircdiv2_clk)), /* b20253 */
70 #if MHZ(48) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
71 	.range      = kSCG_FircRange48M,
72 #elif MHZ(52) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
73 	.range      = kSCG_FircRange52M,
74 #elif MHZ(56) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
75 	.range      = kSCG_FircRange56M,
76 #elif MHZ(60) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
77 	.range      = kSCG_FircRange60M,
78 #else
79 #error Invalid SCG FIRC clock frequency
80 #endif
81 	.trimConfig = NULL
82 };
83 
clk_init(void)84 static ALWAYS_INLINE void clk_init(void)
85 {
86 	const scg_sys_clk_config_t scg_sys_clk_config_safe = {
87 		.divSlow = kSCG_SysClkDivBy4,
88 		.divCore = kSCG_SysClkDivBy1,
89 		.src     = kSCG_SysClkSrcSirc
90 	};
91 	scg_sys_clk_config_t current;
92 
93 	/* Configure SIRC */
94 	CLOCK_InitSirc(&scg_sirc_config);
95 
96 	/* Temporary switch to safe SIRC in order to configure FIRC */
97 	CLOCK_SetRunModeSysClkConfig(&scg_sys_clk_config_safe);
98 	do {
99 		CLOCK_GetCurSysClkConfig(&current);
100 	} while (current.src != scg_sys_clk_config_safe.src);
101 	CLOCK_InitFirc(&scg_firc_config);
102 
103 	/* Only RUN mode supported for now */
104 	CLOCK_SetRunModeSysClkConfig(&scg_sys_clk_config);
105 	do {
106 		CLOCK_GetCurSysClkConfig(&current);
107 	} while (current.src != scg_sys_clk_config.src);
108 
109 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpuart0))
110 	CLOCK_SetIpSrc(kCLOCK_Lpuart0,
111 		       DT_CLOCKS_CELL(DT_NODELABEL(lpuart0), ip_source));
112 #endif
113 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpuart1))
114 	CLOCK_SetIpSrc(kCLOCK_Lpuart1,
115 		       DT_CLOCKS_CELL(DT_NODELABEL(lpuart1), ip_source));
116 #endif
117 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpuart2))
118 	CLOCK_SetIpSrc(kCLOCK_Lpuart2,
119 		       DT_CLOCKS_CELL(DT_NODELABEL(lpuart2), ip_source));
120 #endif
121 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpi2c0))
122 	CLOCK_SetIpSrc(kCLOCK_Lpi2c0,
123 		       DT_CLOCKS_CELL(DT_NODELABEL(lpi2c0), ip_source));
124 #endif
125 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpi2c1))
126 	CLOCK_SetIpSrc(kCLOCK_Lpi2c1,
127 		       DT_CLOCKS_CELL(DT_NODELABEL(lpi2c1), ip_source));
128 #endif
129 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(flexio))
130 	CLOCK_SetIpSrc(kCLOCK_Flexio0,
131 		       DT_CLOCKS_CELL(DT_NODELABEL(flexio), ip_source));
132 #endif
133 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpspi0))
134 	CLOCK_SetIpSrc(kCLOCK_Lpspi0,
135 		       DT_CLOCKS_CELL(DT_NODELABEL(lpspi0), ip_source));
136 #endif
137 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(lpspi1))
138 	CLOCK_SetIpSrc(kCLOCK_Lpspi1,
139 		       DT_CLOCKS_CELL(DT_NODELABEL(lpspi1), ip_source));
140 #endif
141 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(adc0))
142 	CLOCK_SetIpSrc(kCLOCK_Adc0,
143 		       DT_CLOCKS_CELL(DT_NODELABEL(adc0), ip_source));
144 #endif
145 }
146 
soc_early_init_hook(void)147 void soc_early_init_hook(void)
148 
149 {
150 	/* Initialize system clocks and PLL */
151 	clk_init();
152 }
153 
154 #ifdef CONFIG_SOC_RESET_HOOK
155 
soc_reset_hook(void)156 void soc_reset_hook(void)
157 {
158 	/* SystemInit is provided by the NXP SDK */
159 	SystemInit();
160 }
161 
162 #endif /* CONFIG_SOC_RESET_HOOK */
163