xref: /openbmc/u-boot/drivers/clk/imx/clk-imx8.c (revision b9553986)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2018 NXP
4  * Peng Fan <peng.fan@nxp.com>
5  */
6 
7 #include <common.h>
8 #include <clk-uclass.h>
9 #include <dm.h>
10 #include <asm/arch/sci/sci.h>
11 #include <asm/arch/clock.h>
12 #include <dt-bindings/clock/imx8qxp-clock.h>
13 #include <dt-bindings/soc/imx_rsrc.h>
14 #include <misc.h>
15 
16 struct imx8_clks {
17 	ulong id;
18 	const char *name;
19 };
20 
21 #if CONFIG_IS_ENABLED(CMD_CLK)
22 static struct imx8_clks imx8_clk_names[] = {
23 	{ IMX8QXP_A35_DIV, "A35_DIV" },
24 	{ IMX8QXP_I2C0_CLK, "I2C0" },
25 	{ IMX8QXP_I2C1_CLK, "I2C1" },
26 	{ IMX8QXP_I2C2_CLK, "I2C2" },
27 	{ IMX8QXP_I2C3_CLK, "I2C3" },
28 	{ IMX8QXP_UART0_CLK, "UART0" },
29 	{ IMX8QXP_UART1_CLK, "UART1" },
30 	{ IMX8QXP_UART2_CLK, "UART2" },
31 	{ IMX8QXP_UART3_CLK, "UART3" },
32 	{ IMX8QXP_SDHC0_CLK, "SDHC0" },
33 	{ IMX8QXP_SDHC1_CLK, "SDHC1" },
34 	{ IMX8QXP_ENET0_AHB_CLK, "ENET0_AHB" },
35 	{ IMX8QXP_ENET0_IPG_CLK, "ENET0_IPG" },
36 	{ IMX8QXP_ENET0_REF_DIV, "ENET0_REF" },
37 	{ IMX8QXP_ENET0_PTP_CLK, "ENET0_PTP" },
38 	{ IMX8QXP_ENET1_AHB_CLK, "ENET1_AHB" },
39 	{ IMX8QXP_ENET1_IPG_CLK, "ENET1_IPG" },
40 	{ IMX8QXP_ENET1_REF_DIV, "ENET1_REF" },
41 	{ IMX8QXP_ENET1_PTP_CLK, "ENET1_PTP" },
42 };
43 #endif
44 
45 static ulong imx8_clk_get_rate(struct clk *clk)
46 {
47 	sc_pm_clk_t pm_clk;
48 	ulong rate;
49 	u16 resource;
50 	int ret;
51 
52 	debug("%s(#%lu)\n", __func__, clk->id);
53 
54 	switch (clk->id) {
55 	case IMX8QXP_A35_DIV:
56 		resource = SC_R_A35;
57 		pm_clk = SC_PM_CLK_CPU;
58 		break;
59 	case IMX8QXP_I2C0_CLK:
60 		resource = SC_R_I2C_0;
61 		pm_clk = SC_PM_CLK_PER;
62 		break;
63 	case IMX8QXP_I2C1_CLK:
64 		resource = SC_R_I2C_1;
65 		pm_clk = SC_PM_CLK_PER;
66 		break;
67 	case IMX8QXP_I2C2_CLK:
68 		resource = SC_R_I2C_2;
69 		pm_clk = SC_PM_CLK_PER;
70 		break;
71 	case IMX8QXP_I2C3_CLK:
72 		resource = SC_R_I2C_3;
73 		pm_clk = SC_PM_CLK_PER;
74 		break;
75 	case IMX8QXP_SDHC0_IPG_CLK:
76 	case IMX8QXP_SDHC0_CLK:
77 	case IMX8QXP_SDHC0_DIV:
78 		resource = SC_R_SDHC_0;
79 		pm_clk = SC_PM_CLK_PER;
80 		break;
81 	case IMX8QXP_SDHC1_IPG_CLK:
82 	case IMX8QXP_SDHC1_CLK:
83 	case IMX8QXP_SDHC1_DIV:
84 		resource = SC_R_SDHC_1;
85 		pm_clk = SC_PM_CLK_PER;
86 		break;
87 	case IMX8QXP_UART0_IPG_CLK:
88 	case IMX8QXP_UART0_CLK:
89 		resource = SC_R_UART_0;
90 		pm_clk = SC_PM_CLK_PER;
91 		break;
92 	case IMX8QXP_UART1_CLK:
93 		resource = SC_R_UART_1;
94 		pm_clk = SC_PM_CLK_PER;
95 		break;
96 	case IMX8QXP_UART2_CLK:
97 		resource = SC_R_UART_2;
98 		pm_clk = SC_PM_CLK_PER;
99 		break;
100 	case IMX8QXP_UART3_CLK:
101 		resource = SC_R_UART_3;
102 		pm_clk = SC_PM_CLK_PER;
103 		break;
104 	case IMX8QXP_ENET0_IPG_CLK:
105 	case IMX8QXP_ENET0_AHB_CLK:
106 	case IMX8QXP_ENET0_REF_DIV:
107 	case IMX8QXP_ENET0_PTP_CLK:
108 		resource = SC_R_ENET_0;
109 		pm_clk = SC_PM_CLK_PER;
110 		break;
111 	case IMX8QXP_ENET1_IPG_CLK:
112 	case IMX8QXP_ENET1_AHB_CLK:
113 	case IMX8QXP_ENET1_REF_DIV:
114 	case IMX8QXP_ENET1_PTP_CLK:
115 		resource = SC_R_ENET_1;
116 		pm_clk = SC_PM_CLK_PER;
117 		break;
118 	default:
119 		if (clk->id < IMX8QXP_UART0_IPG_CLK ||
120 		    clk->id >= IMX8QXP_CLK_END) {
121 			printf("%s(Invalid clk ID #%lu)\n",
122 			       __func__, clk->id);
123 			return -EINVAL;
124 		}
125 		return -ENOTSUPP;
126 	};
127 
128 	ret = sc_pm_get_clock_rate(-1, resource, pm_clk,
129 				   (sc_pm_clock_rate_t *)&rate);
130 	if (ret) {
131 		printf("%s err %d\n", __func__, ret);
132 		return ret;
133 	}
134 
135 	return rate;
136 }
137 
138 static ulong imx8_clk_set_rate(struct clk *clk, unsigned long rate)
139 {
140 	sc_pm_clk_t pm_clk;
141 	u32 new_rate = rate;
142 	u16 resource;
143 	int ret;
144 
145 	debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
146 
147 	switch (clk->id) {
148 	case IMX8QXP_I2C0_CLK:
149 		resource = SC_R_I2C_0;
150 		pm_clk = SC_PM_CLK_PER;
151 		break;
152 	case IMX8QXP_I2C1_CLK:
153 		resource = SC_R_I2C_1;
154 		pm_clk = SC_PM_CLK_PER;
155 		break;
156 	case IMX8QXP_I2C2_CLK:
157 		resource = SC_R_I2C_2;
158 		pm_clk = SC_PM_CLK_PER;
159 		break;
160 	case IMX8QXP_I2C3_CLK:
161 		resource = SC_R_I2C_3;
162 		pm_clk = SC_PM_CLK_PER;
163 		break;
164 	case IMX8QXP_UART0_CLK:
165 		resource = SC_R_UART_0;
166 		pm_clk = SC_PM_CLK_PER;
167 		break;
168 	case IMX8QXP_UART1_CLK:
169 		resource = SC_R_UART_1;
170 		pm_clk = SC_PM_CLK_PER;
171 		break;
172 	case IMX8QXP_UART2_CLK:
173 		resource = SC_R_UART_2;
174 		pm_clk = SC_PM_CLK_PER;
175 		break;
176 	case IMX8QXP_UART3_CLK:
177 		resource = SC_R_UART_3;
178 		pm_clk = SC_PM_CLK_PER;
179 		break;
180 	case IMX8QXP_SDHC0_IPG_CLK:
181 	case IMX8QXP_SDHC0_CLK:
182 	case IMX8QXP_SDHC0_DIV:
183 		resource = SC_R_SDHC_0;
184 		pm_clk = SC_PM_CLK_PER;
185 		break;
186 	case IMX8QXP_SDHC1_SEL:
187 	case IMX8QXP_SDHC0_SEL:
188 		return 0;
189 	case IMX8QXP_SDHC1_IPG_CLK:
190 	case IMX8QXP_SDHC1_CLK:
191 	case IMX8QXP_SDHC1_DIV:
192 		resource = SC_R_SDHC_1;
193 		pm_clk = SC_PM_CLK_PER;
194 		break;
195 	case IMX8QXP_ENET0_IPG_CLK:
196 	case IMX8QXP_ENET0_AHB_CLK:
197 	case IMX8QXP_ENET0_REF_DIV:
198 	case IMX8QXP_ENET0_PTP_CLK:
199 		resource = SC_R_ENET_0;
200 		pm_clk = SC_PM_CLK_PER;
201 		break;
202 	case IMX8QXP_ENET1_IPG_CLK:
203 	case IMX8QXP_ENET1_AHB_CLK:
204 	case IMX8QXP_ENET1_REF_DIV:
205 	case IMX8QXP_ENET1_PTP_CLK:
206 		resource = SC_R_ENET_1;
207 		pm_clk = SC_PM_CLK_PER;
208 		break;
209 	default:
210 		if (clk->id < IMX8QXP_UART0_IPG_CLK ||
211 		    clk->id >= IMX8QXP_CLK_END) {
212 			printf("%s(Invalid clk ID #%lu)\n",
213 			       __func__, clk->id);
214 			return -EINVAL;
215 		}
216 		return -ENOTSUPP;
217 	};
218 
219 	ret = sc_pm_set_clock_rate(-1, resource, pm_clk, &new_rate);
220 	if (ret) {
221 		printf("%s err %d\n", __func__, ret);
222 		return ret;
223 	}
224 
225 	return new_rate;
226 }
227 
228 static int __imx8_clk_enable(struct clk *clk, bool enable)
229 {
230 	sc_pm_clk_t pm_clk;
231 	u16 resource;
232 	int ret;
233 
234 	debug("%s(#%lu)\n", __func__, clk->id);
235 
236 	switch (clk->id) {
237 	case IMX8QXP_I2C0_CLK:
238 		resource = SC_R_I2C_0;
239 		pm_clk = SC_PM_CLK_PER;
240 		break;
241 	case IMX8QXP_I2C1_CLK:
242 		resource = SC_R_I2C_1;
243 		pm_clk = SC_PM_CLK_PER;
244 		break;
245 	case IMX8QXP_I2C2_CLK:
246 		resource = SC_R_I2C_2;
247 		pm_clk = SC_PM_CLK_PER;
248 		break;
249 	case IMX8QXP_I2C3_CLK:
250 		resource = SC_R_I2C_3;
251 		pm_clk = SC_PM_CLK_PER;
252 		break;
253 	case IMX8QXP_UART0_CLK:
254 		resource = SC_R_UART_0;
255 		pm_clk = SC_PM_CLK_PER;
256 		break;
257 	case IMX8QXP_UART1_CLK:
258 		resource = SC_R_UART_1;
259 		pm_clk = SC_PM_CLK_PER;
260 		break;
261 	case IMX8QXP_UART2_CLK:
262 		resource = SC_R_UART_2;
263 		pm_clk = SC_PM_CLK_PER;
264 		break;
265 	case IMX8QXP_UART3_CLK:
266 		resource = SC_R_UART_3;
267 		pm_clk = SC_PM_CLK_PER;
268 		break;
269 	case IMX8QXP_SDHC0_IPG_CLK:
270 	case IMX8QXP_SDHC0_CLK:
271 	case IMX8QXP_SDHC0_DIV:
272 		resource = SC_R_SDHC_0;
273 		pm_clk = SC_PM_CLK_PER;
274 		break;
275 	case IMX8QXP_SDHC1_IPG_CLK:
276 	case IMX8QXP_SDHC1_CLK:
277 	case IMX8QXP_SDHC1_DIV:
278 		resource = SC_R_SDHC_1;
279 		pm_clk = SC_PM_CLK_PER;
280 		break;
281 	case IMX8QXP_ENET0_IPG_CLK:
282 	case IMX8QXP_ENET0_AHB_CLK:
283 	case IMX8QXP_ENET0_REF_DIV:
284 	case IMX8QXP_ENET0_PTP_CLK:
285 		resource = SC_R_ENET_0;
286 		pm_clk = SC_PM_CLK_PER;
287 		break;
288 	case IMX8QXP_ENET1_IPG_CLK:
289 	case IMX8QXP_ENET1_AHB_CLK:
290 	case IMX8QXP_ENET1_REF_DIV:
291 	case IMX8QXP_ENET1_PTP_CLK:
292 		resource = SC_R_ENET_1;
293 		pm_clk = SC_PM_CLK_PER;
294 		break;
295 	default:
296 		if (clk->id < IMX8QXP_UART0_IPG_CLK ||
297 		    clk->id >= IMX8QXP_CLK_END) {
298 			printf("%s(Invalid clk ID #%lu)\n",
299 			       __func__, clk->id);
300 			return -EINVAL;
301 		}
302 		return -ENOTSUPP;
303 	}
304 
305 	ret = sc_pm_clock_enable(-1, resource, pm_clk, enable, 0);
306 	if (ret) {
307 		printf("%s err %d\n", __func__, ret);
308 		return ret;
309 	}
310 
311 	return 0;
312 }
313 
314 static int imx8_clk_disable(struct clk *clk)
315 {
316 	return __imx8_clk_enable(clk, 0);
317 }
318 
319 static int imx8_clk_enable(struct clk *clk)
320 {
321 	return __imx8_clk_enable(clk, 1);
322 }
323 
324 #if CONFIG_IS_ENABLED(CMD_CLK)
325 int soc_clk_dump(void)
326 {
327 	struct udevice *dev;
328 	struct clk clk;
329 	unsigned long rate;
330 	int i, ret;
331 
332 	ret = uclass_get_device_by_driver(UCLASS_CLK,
333 					  DM_GET_DRIVER(imx8_clk), &dev);
334 	if (ret)
335 		return ret;
336 
337 	printf("Clk\t\tHz\n");
338 
339 	for (i = 0; i < ARRAY_SIZE(imx8_clk_names); i++) {
340 		clk.id = imx8_clk_names[i].id;
341 		ret = clk_request(dev, &clk);
342 		if (ret < 0) {
343 			debug("%s clk_request() failed: %d\n", __func__, ret);
344 			continue;
345 		}
346 
347 		ret = clk_get_rate(&clk);
348 		rate = ret;
349 
350 		clk_free(&clk);
351 
352 		if (ret == -ENOTSUPP) {
353 			printf("clk ID %lu not supported yet\n",
354 			       imx8_clk_names[i].id);
355 			continue;
356 		}
357 		if (ret < 0) {
358 			printf("%s %lu: get_rate err: %d\n",
359 			       __func__, imx8_clk_names[i].id, ret);
360 			continue;
361 		}
362 
363 		printf("%s(%3lu):\t%lu\n",
364 		       imx8_clk_names[i].name, imx8_clk_names[i].id, rate);
365 	}
366 
367 	return 0;
368 }
369 #endif
370 
371 static struct clk_ops imx8_clk_ops = {
372 	.set_rate = imx8_clk_set_rate,
373 	.get_rate = imx8_clk_get_rate,
374 	.enable = imx8_clk_enable,
375 	.disable = imx8_clk_disable,
376 };
377 
378 static int imx8_clk_probe(struct udevice *dev)
379 {
380 	return 0;
381 }
382 
383 static const struct udevice_id imx8_clk_ids[] = {
384 	{ .compatible = "fsl,imx8qxp-clk" },
385 	{ },
386 };
387 
388 U_BOOT_DRIVER(imx8_clk) = {
389 	.name = "clk_imx8",
390 	.id = UCLASS_CLK,
391 	.of_match = imx8_clk_ids,
392 	.ops = &imx8_clk_ops,
393 	.probe = imx8_clk_probe,
394 	.flags = DM_FLAG_PRE_RELOC,
395 };
396