Merge branch 'master' of git://git.denx.de/u-boot-usb
[oweals/u-boot.git] / drivers / clk / imx / clk-imx8.c
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 };