1 /*
2 * Copyright 2017,2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_imx_ccm
8 #include <errno.h>
9 #include <zephyr/arch/cpu.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/dt-bindings/clock/imx_ccm.h>
13 #include <fsl_clock.h>
14
15 #if defined(CONFIG_SOC_MIMX8QM6_ADSP) || defined(CONFIG_SOC_MIMX8QX6_ADSP)
16 #include <main/ipc.h>
17 #endif
18
19 #define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(clock_control);
22
23 #ifdef CONFIG_SPI_MCUX_LPSPI
24 static const clock_name_t lpspi_clocks[] = {
25 kCLOCK_Usb1PllPfd1Clk,
26 kCLOCK_Usb1PllPfd0Clk,
27 kCLOCK_SysPllClk,
28 kCLOCK_SysPllPfd2Clk,
29 };
30 #endif
31 #ifdef CONFIG_UART_MCUX_IUART
32 static const clock_root_control_t uart_clk_root[] = {
33 kCLOCK_RootUart1,
34 kCLOCK_RootUart2,
35 kCLOCK_RootUart3,
36 kCLOCK_RootUart4,
37 };
38
39 static const clock_ip_name_t uart_clocks[] = {
40 kCLOCK_Uart1,
41 kCLOCK_Uart2,
42 kCLOCK_Uart3,
43 kCLOCK_Uart4,
44 };
45 #endif
46
47 #ifdef CONFIG_UART_MCUX_LPUART
48
49 #ifdef CONFIG_SOC_MIMX8QM6_ADSP
50 static const clock_ip_name_t lpuart_clocks[] = {
51 kCLOCK_DMA_Lpuart0,
52 kCLOCK_DMA_Lpuart1,
53 kCLOCK_DMA_Lpuart2,
54 kCLOCK_DMA_Lpuart3,
55 kCLOCK_DMA_Lpuart4,
56 };
57
58 static const uint32_t lpuart_rate = MHZ(80);
59 #endif /* CONFIG_SOC_MIMX8QM6_ADSP */
60
61 #ifdef CONFIG_SOC_MIMX8QX6_ADSP
62 static const clock_ip_name_t lpuart_clocks[] = {
63 kCLOCK_DMA_Lpuart0,
64 kCLOCK_DMA_Lpuart1,
65 kCLOCK_DMA_Lpuart2,
66 kCLOCK_DMA_Lpuart3,
67 };
68
69 static const uint32_t lpuart_rate = MHZ(80);
70 #endif /* CONFIG_SOC_MIMX8QX6_ADSP */
71
72 #endif /* CONFIG_UART_MCUX_LPUART */
73
74 #ifdef CONFIG_DAI_NXP_SAI
75 #if defined(CONFIG_SOC_MIMX8QX6_ADSP) || defined(CONFIG_SOC_MIMX8QM6_ADSP)
76 static const clock_ip_name_t sai_clocks[] = {
77 kCLOCK_AUDIO_Sai1,
78 kCLOCK_AUDIO_Sai2,
79 kCLOCK_AUDIO_Sai3,
80 };
81 #endif
82 #endif /* CONFIG_DAI_NXP_SAI */
83
84
mcux_ccm_on(const struct device * dev,clock_control_subsys_t sub_system)85 static int mcux_ccm_on(const struct device *dev,
86 clock_control_subsys_t sub_system)
87 {
88 uint32_t clock_name = (uintptr_t)sub_system;
89 uint32_t instance = clock_name & IMX_CCM_INSTANCE_MASK;
90
91 switch (clock_name) {
92 #ifdef CONFIG_UART_MCUX_IUART
93 case IMX_CCM_UART1_CLK:
94 case IMX_CCM_UART2_CLK:
95 case IMX_CCM_UART3_CLK:
96 case IMX_CCM_UART4_CLK:
97 CLOCK_EnableClock(uart_clocks[instance]);
98 return 0;
99 #endif
100
101 #if defined(CONFIG_UART_MCUX_LPUART) && defined(CONFIG_SOC_MIMX8QM6_ADSP)
102 case IMX_CCM_LPUART1_CLK:
103 case IMX_CCM_LPUART2_CLK:
104 case IMX_CCM_LPUART3_CLK:
105 case IMX_CCM_LPUART4_CLK:
106 case IMX_CCM_LPUART5_CLK:
107 CLOCK_EnableClock(lpuart_clocks[instance]);
108 return 0;
109 #endif
110
111 #if defined(CONFIG_UART_MCUX_LPUART) && defined(CONFIG_SOC_MIMX8QX6_ADSP)
112 case IMX_CCM_LPUART1_CLK:
113 case IMX_CCM_LPUART2_CLK:
114 case IMX_CCM_LPUART3_CLK:
115 case IMX_CCM_LPUART4_CLK:
116 CLOCK_EnableClock(lpuart_clocks[instance]);
117 return 0;
118 #endif
119
120 #ifdef CONFIG_DAI_NXP_SAI
121 #if defined(CONFIG_SOC_MIMX8QM6_ADSP) || defined(CONFIG_SOC_MIMX8QX6_ADSP)
122 case IMX_CCM_SAI1_CLK:
123 case IMX_CCM_SAI2_CLK:
124 case IMX_CCM_SAI3_CLK:
125 CLOCK_EnableClock(sai_clocks[instance]);
126 return 0;
127 #endif
128 #endif /* CONFIG_DAI_NXP_SAI */
129
130 #if defined(CONFIG_ETH_NXP_ENET)
131 #ifdef CONFIG_SOC_SERIES_IMX8M
132 #define ENET_CLOCK kCLOCK_Enet1
133 #else
134 #define ENET_CLOCK kCLOCK_Enet
135 #endif
136 case IMX_CCM_ENET_CLK:
137 CLOCK_EnableClock(ENET_CLOCK);
138 return 0;
139 #endif
140 default:
141 (void)instance;
142 return 0;
143 }
144 }
145
mcux_ccm_off(const struct device * dev,clock_control_subsys_t sub_system)146 static int mcux_ccm_off(const struct device *dev,
147 clock_control_subsys_t sub_system)
148 {
149 uint32_t clock_name = (uintptr_t)sub_system;
150 uint32_t instance = clock_name & IMX_CCM_INSTANCE_MASK;
151
152 switch (clock_name) {
153 #ifdef CONFIG_UART_MCUX_IUART
154 case IMX_CCM_UART1_CLK:
155 case IMX_CCM_UART2_CLK:
156 case IMX_CCM_UART3_CLK:
157 case IMX_CCM_UART4_CLK:
158 CLOCK_DisableClock(uart_clocks[instance]);
159 return 0;
160 #endif
161
162 #ifdef CONFIG_DAI_NXP_SAI
163 #if defined(CONFIG_SOC_MIMX8QM6_ADSP) || defined(CONFIG_SOC_MIMX8QX6_ADSP)
164 case IMX_CCM_SAI1_CLK:
165 case IMX_CCM_SAI2_CLK:
166 case IMX_CCM_SAI3_CLK:
167 CLOCK_DisableClock(sai_clocks[instance]);
168 return 0;
169 #endif
170 #endif /* CONFIG_DAI_NXP_SAI */
171 default:
172 (void)instance;
173 return 0;
174 }
175 }
176
mcux_ccm_get_subsys_rate(const struct device * dev,clock_control_subsys_t sub_system,uint32_t * rate)177 static int mcux_ccm_get_subsys_rate(const struct device *dev,
178 clock_control_subsys_t sub_system,
179 uint32_t *rate)
180 {
181 uint32_t clock_name = (uintptr_t)sub_system;
182
183 switch (clock_name) {
184
185 #ifdef CONFIG_I2C_MCUX_LPI2C
186 case IMX_CCM_LPI2C_CLK:
187 if (CLOCK_GetMux(kCLOCK_Lpi2cMux) == 0) {
188 *rate = CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 8
189 / (CLOCK_GetDiv(kCLOCK_Lpi2cDiv) + 1);
190 } else {
191 *rate = CLOCK_GetOscFreq()
192 / (CLOCK_GetDiv(kCLOCK_Lpi2cDiv) + 1);
193 }
194
195 break;
196 #endif
197
198 #ifdef CONFIG_SPI_MCUX_LPSPI
199 case IMX_CCM_LPSPI_CLK:
200 {
201 uint32_t lpspi_mux = CLOCK_GetMux(kCLOCK_LpspiMux);
202 clock_name_t lpspi_clock = lpspi_clocks[lpspi_mux];
203
204 *rate = CLOCK_GetFreq(lpspi_clock)
205 / (CLOCK_GetDiv(kCLOCK_LpspiDiv) + 1);
206 break;
207 }
208 #endif
209
210 #ifdef CONFIG_UART_MCUX_LPUART
211
212 #if defined(CONFIG_SOC_MIMX8QM6_ADSP)
213 case IMX_CCM_LPUART1_CLK:
214 case IMX_CCM_LPUART2_CLK:
215 case IMX_CCM_LPUART3_CLK:
216 case IMX_CCM_LPUART4_CLK:
217 case IMX_CCM_LPUART5_CLK:
218 uint32_t instance = clock_name & IMX_CCM_INSTANCE_MASK;
219
220 CLOCK_SetIpFreq(lpuart_clocks[instance], lpuart_rate);
221 *rate = CLOCK_GetIpFreq(lpuart_clocks[instance]);
222 break;
223
224 #elif defined(CONFIG_SOC_MIMX8QX6_ADSP)
225 case IMX_CCM_LPUART1_CLK:
226 case IMX_CCM_LPUART2_CLK:
227 case IMX_CCM_LPUART3_CLK:
228 case IMX_CCM_LPUART4_CLK:
229 uint32_t instance = clock_name & IMX_CCM_INSTANCE_MASK;
230
231 CLOCK_SetIpFreq(lpuart_clocks[instance], lpuart_rate);
232 *rate = CLOCK_GetIpFreq(lpuart_clocks[instance]);
233 break;
234
235 #else
236 case IMX_CCM_LPUART_CLK:
237 if (CLOCK_GetMux(kCLOCK_UartMux) == 0) {
238 *rate = CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6
239 / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1);
240 } else {
241 *rate = CLOCK_GetOscFreq()
242 / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1);
243 }
244
245 break;
246 #endif
247 #endif
248
249 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usdhc1)) && CONFIG_IMX_USDHC
250 case IMX_CCM_USDHC1_CLK:
251 *rate = CLOCK_GetSysPfdFreq(kCLOCK_Pfd0) /
252 (CLOCK_GetDiv(kCLOCK_Usdhc1Div) + 1U);
253 break;
254 #endif
255
256 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(usdhc2)) && CONFIG_IMX_USDHC
257 case IMX_CCM_USDHC2_CLK:
258 *rate = CLOCK_GetSysPfdFreq(kCLOCK_Pfd0) /
259 (CLOCK_GetDiv(kCLOCK_Usdhc2Div) + 1U);
260 break;
261 #endif
262
263 #ifdef CONFIG_DMA_MCUX_EDMA
264 case IMX_CCM_EDMA_CLK:
265 *rate = CLOCK_GetIpgFreq();
266 break;
267 #endif
268
269 #ifdef CONFIG_PWM_MCUX
270 case IMX_CCM_PWM_CLK:
271 *rate = CLOCK_GetIpgFreq();
272 break;
273 #endif
274
275 #ifdef CONFIG_ETH_NXP_ENET
276 case IMX_CCM_ENET_CLK:
277 #ifdef CONFIG_SOC_SERIES_IMX8M
278 *rate = CLOCK_GetFreq(kCLOCK_EnetIpgClk);
279 #else
280 *rate = CLOCK_GetIpgFreq();
281 #endif
282 #endif
283 break;
284
285 #ifdef CONFIG_PTP_CLOCK_NXP_ENET
286 case IMX_CCM_ENET_PLL:
287 *rate = CLOCK_GetPllFreq(kCLOCK_PllEnet);
288 break;
289 #endif
290
291 #ifdef CONFIG_UART_MCUX_IUART
292 case IMX_CCM_UART1_CLK:
293 case IMX_CCM_UART2_CLK:
294 case IMX_CCM_UART3_CLK:
295 case IMX_CCM_UART4_CLK:
296 {
297 uint32_t instance = clock_name & IMX_CCM_INSTANCE_MASK;
298 clock_root_control_t clk_root = uart_clk_root[instance];
299 uint32_t uart_mux = CLOCK_GetRootMux(clk_root);
300
301 if (uart_mux == 0) {
302 *rate = MHZ(24);
303 } else if (uart_mux == 1) {
304 *rate = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) /
305 (CLOCK_GetRootPreDivider(clk_root)) /
306 (CLOCK_GetRootPostDivider(clk_root)) /
307 10;
308 }
309
310 } break;
311 #endif
312
313 #ifdef CONFIG_CAN_MCUX_FLEXCAN
314 case IMX_CCM_CAN_CLK:
315 {
316 uint32_t can_mux = CLOCK_GetMux(kCLOCK_CanMux);
317
318 if (can_mux == 0) {
319 *rate = CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 8
320 / (CLOCK_GetDiv(kCLOCK_CanDiv) + 1);
321 } else if (can_mux == 1) {
322 *rate = CLOCK_GetOscFreq()
323 / (CLOCK_GetDiv(kCLOCK_CanDiv) + 1);
324 } else {
325 *rate = CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6
326 / (CLOCK_GetDiv(kCLOCK_CanDiv) + 1);
327 }
328 } break;
329 #endif
330
331 #ifdef CONFIG_COUNTER_MCUX_GPT
332 case IMX_CCM_GPT_CLK:
333 *rate = CLOCK_GetFreq(kCLOCK_PerClk);
334 break;
335 #ifdef CONFIG_SOC_SERIES_IMX8M
336 case IMX_CCM_GPT_IPG_CLK:
337 {
338 uint32_t mux = CLOCK_GetRootMux(kCLOCK_RootGpt1);
339
340 if (mux == 0) {
341 *rate = OSC24M_CLK_FREQ;
342 } else {
343 *rate = 0;
344 }
345 } break;
346 #endif
347 #endif
348
349 #ifdef CONFIG_COUNTER_MCUX_QTMR
350 case IMX_CCM_QTMR_CLK:
351 *rate = CLOCK_GetIpgFreq();
352 break;
353 #endif
354
355 #ifdef CONFIG_I2S_MCUX_SAI
356 case IMX_CCM_SAI1_CLK:
357 *rate = CLOCK_GetFreq(kCLOCK_AudioPllClk)
358 / (CLOCK_GetDiv(kCLOCK_Sai1PreDiv) + 1)
359 / (CLOCK_GetDiv(kCLOCK_Sai1Div) + 1);
360 break;
361 case IMX_CCM_SAI2_CLK:
362 *rate = CLOCK_GetFreq(kCLOCK_AudioPllClk)
363 / (CLOCK_GetDiv(kCLOCK_Sai2PreDiv) + 1)
364 / (CLOCK_GetDiv(kCLOCK_Sai2Div) + 1);
365 break;
366 case IMX_CCM_SAI3_CLK:
367 *rate = CLOCK_GetFreq(kCLOCK_AudioPllClk)
368 / (CLOCK_GetDiv(kCLOCK_Sai3PreDiv) + 1)
369 / (CLOCK_GetDiv(kCLOCK_Sai3Div) + 1);
370 break;
371 #endif
372 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(flexspi))
373 case IMX_CCM_FLEXSPI_CLK:
374 *rate = CLOCK_GetClockRootFreq(kCLOCK_FlexspiClkRoot);
375 break;
376 #endif
377 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(flexspi2))
378 case IMX_CCM_FLEXSPI2_CLK:
379 *rate = CLOCK_GetClockRootFreq(kCLOCK_Flexspi2ClkRoot);
380 break;
381 #endif
382 #ifdef CONFIG_COUNTER_NXP_PIT
383 case IMX_CCM_PIT_CLK:
384 *rate = CLOCK_GetFreq(kCLOCK_PerClk);
385 break;
386 #endif
387 #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(flexio1)) && CONFIG_MCUX_FLEXIO
388 case IMX_CCM_FLEXIO1_CLK:
389 {
390 uint32_t flexio_mux = CLOCK_GetMux(kCLOCK_Flexio1Mux);
391 uint32_t source_clk_freq = 0;
392
393 if (flexio_mux == 0) {
394 source_clk_freq = CLOCK_GetPllFreq(kCLOCK_PllAudio);
395 } else if (flexio_mux == 1) {
396 source_clk_freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd2);
397 #ifdef PLL_VIDEO_OFFSET /* fsl_clock.h */
398 } else if (flexio_mux == 2) {
399 source_clk_freq = CLOCK_GetPllFreq(kCLOCK_PllVideo);
400 #endif
401 } else {
402 source_clk_freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1);
403 }
404
405 *rate = source_clk_freq / (CLOCK_GetDiv(kCLOCK_Flexio1PreDiv) + 1)
406 / (CLOCK_GetDiv(kCLOCK_Flexio1Div) + 1);
407 } break;
408 #endif
409 #if (DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(flexio2)) \
410 || DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(flexio3))) && CONFIG_MCUX_FLEXIO
411 case IMX_CCM_FLEXIO2_3_CLK:
412 {
413 uint32_t flexio_mux = CLOCK_GetMux(kCLOCK_Flexio2Mux);
414 uint32_t source_clk_freq = 0;
415
416 if (flexio_mux == 0) {
417 source_clk_freq = CLOCK_GetPllFreq(kCLOCK_PllAudio);
418 } else if (flexio_mux == 1) {
419 source_clk_freq = CLOCK_GetUsb1PfdFreq(kCLOCK_Pfd2);
420 #ifdef PLL_VIDEO_OFFSET /* fsl_clock.h */
421 } else if (flexio_mux == 2) {
422 source_clk_freq = CLOCK_GetPllFreq(kCLOCK_PllVideo);
423 #endif
424 } else {
425 source_clk_freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1);
426 }
427
428 *rate = source_clk_freq / (CLOCK_GetDiv(kCLOCK_Flexio2PreDiv) + 1)
429 / (CLOCK_GetDiv(kCLOCK_Flexio2Div) + 1);
430 } break;
431 #endif
432
433 #ifdef CONFIG_SPI_MCUX_ECSPI
434 case IMX_CCM_ECSPI1_CLK:
435 *rate = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) /
436 (CLOCK_GetRootPreDivider(kCLOCK_RootEcspi1)) /
437 (CLOCK_GetRootPostDivider(kCLOCK_RootEcspi1));
438 break;
439 case IMX_CCM_ECSPI2_CLK:
440 *rate = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) /
441 (CLOCK_GetRootPreDivider(kCLOCK_RootEcspi2)) /
442 (CLOCK_GetRootPostDivider(kCLOCK_RootEcspi2));
443 break;
444 case IMX_CCM_ECSPI3_CLK:
445 *rate = CLOCK_GetPllFreq(kCLOCK_SystemPll1Ctrl) /
446 (CLOCK_GetRootPreDivider(kCLOCK_RootEcspi3)) /
447 (CLOCK_GetRootPostDivider(kCLOCK_RootEcspi3));
448 break;
449 #endif /* CONFIG_SPI_MCUX_ECSPI */
450 }
451
452 return 0;
453 }
454
455 /*
456 * Since this function is used to reclock the FlexSPI when running in
457 * XIP, it must be located in RAM when MEMC Flexspi driver is enabled.
458 */
459 #ifdef CONFIG_MEMC_MCUX_FLEXSPI
460 #define CCM_SET_FUNC_ATTR __ramfunc
461 #else
462 #define CCM_SET_FUNC_ATTR
463 #endif
464
mcux_ccm_set_subsys_rate(const struct device * dev,clock_control_subsys_t subsys,clock_control_subsys_rate_t rate)465 static int CCM_SET_FUNC_ATTR mcux_ccm_set_subsys_rate(const struct device *dev,
466 clock_control_subsys_t subsys,
467 clock_control_subsys_rate_t rate)
468 {
469 uint32_t clock_name = (uintptr_t)subsys;
470 uint32_t clock_rate = (uintptr_t)rate;
471
472 switch (clock_name) {
473 case IMX_CCM_FLEXSPI_CLK:
474 __fallthrough;
475 case IMX_CCM_FLEXSPI2_CLK:
476 #if defined(CONFIG_SOC_SERIES_IMXRT10XX) && defined(CONFIG_MEMC_MCUX_FLEXSPI)
477 /* The SOC is using the FlexSPI for XIP. Therefore,
478 * the FlexSPI itself must be managed within the function,
479 * which is SOC specific.
480 */
481 return flexspi_clock_set_freq(clock_name, clock_rate);
482 #endif
483 default:
484 /* Silence unused variable warning */
485 ARG_UNUSED(clock_rate);
486 return -ENOTSUP;
487 }
488 }
489
490
491
492 static DEVICE_API(clock_control, mcux_ccm_driver_api) = {
493 .on = mcux_ccm_on,
494 .off = mcux_ccm_off,
495 .get_rate = mcux_ccm_get_subsys_rate,
496 .set_rate = mcux_ccm_set_subsys_rate,
497 };
498
mcux_ccm_init(const struct device * dev)499 static int mcux_ccm_init(const struct device *dev)
500 {
501 #if defined(CONFIG_SOC_MIMX8QM6_ADSP) || defined(CONFIG_SOC_MIMX8QX6_ADSP)
502 sc_ipc_t ipc_handle;
503 int ret;
504
505 ret = sc_ipc_open(&ipc_handle, DT_REG_ADDR(DT_NODELABEL(scu_mu)));
506 if (ret != SC_ERR_NONE) {
507 return -ENODEV;
508 }
509
510 CLOCK_Init(ipc_handle);
511 #endif
512 return 0;
513 }
514
515 DEVICE_DT_INST_DEFINE(0, mcux_ccm_init, NULL, NULL, NULL,
516 PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
517 &mcux_ccm_driver_api);
518