1 /*
2  * Copyright (c) 2021, NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_imx_ccm_rev2
8 #include <errno.h>
9 #include <zephyr/drivers/clock_control.h>
10 #include <zephyr/dt-bindings/clock/imx_ccm_rev2.h>
11 #include <fsl_clock.h>
12 
13 #define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(clock_control);
16 
mcux_ccm_on(const struct device * dev,clock_control_subsys_t sub_system)17 static int mcux_ccm_on(const struct device *dev,
18 				  clock_control_subsys_t sub_system)
19 {
20 #ifdef CONFIG_ETH_NXP_ENET
21 	if ((uint32_t)sub_system == IMX_CCM_ENET_CLK) {
22 		CLOCK_EnableClock(kCLOCK_Enet);
23 	}
24 #endif
25 	return 0;
26 }
27 
mcux_ccm_off(const struct device * dev,clock_control_subsys_t sub_system)28 static int mcux_ccm_off(const struct device *dev,
29 				   clock_control_subsys_t sub_system)
30 {
31 	return 0;
32 }
33 
mcux_ccm_get_subsys_rate(const struct device * dev,clock_control_subsys_t sub_system,uint32_t * rate)34 static int mcux_ccm_get_subsys_rate(const struct device *dev,
35 					clock_control_subsys_t sub_system,
36 					uint32_t *rate)
37 {
38 	uint32_t clock_name = (size_t) sub_system;
39 	uint32_t clock_root, peripheral, instance;
40 
41 	peripheral = (clock_name & IMX_CCM_PERIPHERAL_MASK);
42 	instance = (clock_name & IMX_CCM_INSTANCE_MASK);
43 	switch (peripheral) {
44 #ifdef CONFIG_I2C_MCUX_LPI2C
45 	case IMX_CCM_LPI2C1_CLK:
46 		clock_root = kCLOCK_Root_Lpi2c1 + instance;
47 		break;
48 #endif
49 
50 #ifdef CONFIG_SPI_MCUX_LPSPI
51 	case IMX_CCM_LPSPI1_CLK:
52 		clock_root = kCLOCK_Root_Lpspi1 + instance;
53 		break;
54 #endif
55 
56 #ifdef CONFIG_UART_MCUX_LPUART
57 	case IMX_CCM_LPUART1_CLK:
58 	case IMX_CCM_LPUART2_CLK:
59 		clock_root = kCLOCK_Root_Lpuart1 + instance;
60 		break;
61 #endif
62 
63 #if CONFIG_IMX_USDHC
64 	case IMX_CCM_USDHC1_CLK:
65 	case IMX_CCM_USDHC2_CLK:
66 		clock_root = kCLOCK_Root_Usdhc1 + instance;
67 		break;
68 #endif
69 
70 #ifdef CONFIG_DMA_MCUX_EDMA
71 	case IMX_CCM_EDMA_CLK:
72 		clock_root = kCLOCK_Root_Bus;
73 		break;
74 	case IMX_CCM_EDMA_LPSR_CLK:
75 		clock_root = kCLOCK_Root_Bus_Lpsr;
76 		break;
77 #endif
78 
79 #ifdef CONFIG_PWM_MCUX
80 	case IMX_CCM_PWM_CLK:
81 		clock_root = kCLOCK_Root_Bus;
82 		break;
83 #endif
84 
85 #ifdef CONFIG_CAN_MCUX_FLEXCAN
86 	case IMX_CCM_CAN1_CLK:
87 		clock_root = kCLOCK_Root_Can1 + instance;
88 		break;
89 #endif
90 
91 #ifdef CONFIG_COUNTER_MCUX_GPT
92 	case IMX_CCM_GPT_CLK:
93 		clock_root = kCLOCK_Root_Gpt1 + instance;
94 		break;
95 #endif
96 
97 #ifdef CONFIG_I2S_MCUX_SAI
98 	case IMX_CCM_SAI1_CLK:
99 		clock_root =  kCLOCK_Root_Sai1;
100 		break;
101 	case IMX_CCM_SAI2_CLK:
102 		clock_root =  kCLOCK_Root_Sai2;
103 		break;
104 	case IMX_CCM_SAI3_CLK:
105 		clock_root =  kCLOCK_Root_Sai3;
106 		break;
107 	case IMX_CCM_SAI4_CLK:
108 		clock_root =  kCLOCK_Root_Sai4;
109 		break;
110 #endif
111 
112 #ifdef CONFIG_ETH_NXP_ENET
113 	case IMX_CCM_ENET_CLK:
114 		clock_root = kCLOCK_Root_Bus;
115 		break;
116 #endif
117 
118 #if defined(CONFIG_SOC_MIMX93_A55) && defined(CONFIG_DAI_NXP_SAI)
119 	case IMX_CCM_SAI1_CLK:
120 	case IMX_CCM_SAI2_CLK:
121 	case IMX_CCM_SAI3_CLK:
122 		clock_root = kCLOCK_Root_Sai1 + instance;
123 		uint32_t mux = CLOCK_GetRootClockMux(clock_root);
124 		uint32_t divider = CLOCK_GetRootClockDiv(clock_root);
125 
126 		/* assumption: SAI's SRC is AUDIO_PLL */
127 		if (mux != 1) {
128 			return -EINVAL;
129 		}
130 
131 		/* assumption: AUDIO_PLL's frequency is 393216000 Hz */
132 		*rate = 393216000 / divider;
133 
134 		return 0;
135 #endif
136 #ifdef CONFIG_MEMC_MCUX_FLEXSPI
137 	case IMX_CCM_FLEXSPI_CLK:
138 		clock_root = kCLOCK_Root_Flexspi1;
139 		break;
140 	case IMX_CCM_FLEXSPI2_CLK:
141 		clock_root = kCLOCK_Root_Flexspi2;
142 		break;
143 #endif
144 	default:
145 		return -EINVAL;
146 	}
147 #ifdef CONFIG_SOC_MIMX93_A55
148 	*rate = CLOCK_GetIpFreq(clock_root);
149 #else
150 	*rate = CLOCK_GetRootClockFreq(clock_root);
151 #endif
152 	return 0;
153 }
154 
155 /*
156  * Since this function is used to reclock the FlexSPI when running in
157  * XIP, it must be located in RAM when MEMC driver is enabled.
158  */
159 #ifdef CONFIG_MEMC_MCUX_FLEXSPI
160 #define CCM_SET_FUNC_ATTR __ramfunc
161 #else
162 #define CCM_SET_FUNC_ATTR
163 #endif
164 
mcux_ccm_set_subsys_rate(const struct device * dev,clock_control_subsys_t subsys,clock_control_subsys_rate_t rate)165 static int CCM_SET_FUNC_ATTR mcux_ccm_set_subsys_rate(const struct device *dev,
166 			clock_control_subsys_t subsys,
167 			clock_control_subsys_rate_t rate)
168 {
169 	uint32_t clock_name = (uintptr_t)subsys;
170 	uint32_t clock_rate = (uintptr_t)rate;
171 
172 	switch (clock_name) {
173 	case IMX_CCM_FLEXSPI_CLK:
174 		__fallthrough;
175 	case IMX_CCM_FLEXSPI2_CLK:
176 #if defined(CONFIG_SOC_SERIES_IMX_RT11XX) && defined(CONFIG_MEMC_MCUX_FLEXSPI)
177 		/* The SOC is using the FlexSPI for XIP. Therefore,
178 		 * the FlexSPI itself must be managed within the function,
179 		 * which is SOC specific.
180 		 */
181 		return flexspi_clock_set_freq(clock_name, clock_rate);
182 #endif
183 	default:
184 		/* Silence unused variable warning */
185 		ARG_UNUSED(clock_rate);
186 		return -ENOTSUP;
187 	}
188 }
189 
190 static const struct clock_control_driver_api mcux_ccm_driver_api = {
191 	.on = mcux_ccm_on,
192 	.off = mcux_ccm_off,
193 	.get_rate = mcux_ccm_get_subsys_rate,
194 	.set_rate = mcux_ccm_set_subsys_rate,
195 };
196 
197 DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, PRE_KERNEL_1,
198 		      CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
199 		      &mcux_ccm_driver_api);
200