imx6_ccm.c revision 1.4 1 /* $NetBSD: imx6_ccm.c,v 1.4 2023/05/04 13:25:07 bouyer Exp $ */
2
3 /*
4 * Copyright (c) 2010-2012, 2014 Genetec Corporation. All rights reserved.
5 * Written by Hashimoto Kenichi for Genetec Corporation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28 /*
29 * Clock Controller Module (CCM) for i.MX6
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: imx6_ccm.c,v 1.4 2023/05/04 13:25:07 bouyer Exp $");
34
35 #include "opt_imx.h"
36 #include "opt_cputypes.h"
37
38 #include "locators.h"
39
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/bus.h>
43 #include <sys/device.h>
44 #include <sys/sysctl.h>
45 #include <sys/cpufreq.h>
46 #include <sys/param.h>
47
48 #include <machine/cpu.h>
49
50 #include <arm/nxp/imx6_ccmvar.h>
51 #include <arm/nxp/imx6_ccmreg.h>
52
53 #include <dev/clk/clk_backend.h>
54
55 static void imxccm_init_clocks(struct imx6ccm_softc *,
56 struct imxccm_init_parent *);
57 static struct clk *imxccm_clk_get(void *, const char *);
58 static void imxccm_clk_put(void *, struct clk *);
59 static u_int imxccm_clk_get_rate(void *, struct clk *);
60 static int imxccm_clk_set_rate(void *, struct clk *, u_int);
61 static int imxccm_clk_enable(void *, struct clk *);
62 static int imxccm_clk_disable(void *, struct clk *);
63 static int imxccm_clk_set_parent(void *, struct clk *, struct clk *);
64 static struct clk *imxccm_clk_get_parent(void *, struct clk *);
65
66 static const struct clk_funcs imxccm_clk_funcs = {
67 .get = imxccm_clk_get,
68 .put = imxccm_clk_put,
69 .get_rate = imxccm_clk_get_rate,
70 .set_rate = imxccm_clk_set_rate,
71 .enable = imxccm_clk_enable,
72 .disable = imxccm_clk_disable,
73 .set_parent = imxccm_clk_set_parent,
74 .get_parent = imxccm_clk_get_parent,
75 };
76
77 void
78 imx6ccm_attach_common(device_t self, struct imx6_clk *imx6_clks, int size,
79 struct imxccm_init_parent *imxccm_init_parents)
80 {
81 struct imx6ccm_softc * const sc = device_private(self);
82
83 sc->sc_dev = self;
84 sc->sc_imx6_clks = imx6_clks;
85 sc->sc_imx6_clksize = size;
86
87 sc->sc_clkdom.name = device_xname(self);
88 sc->sc_clkdom.funcs = &imxccm_clk_funcs;
89 sc->sc_clkdom.priv = sc;
90 for (u_int n = 0; n < size; n++) {
91 imx6_clks[n].base.domain = &sc->sc_clkdom;
92 clk_attach(&imx6_clks[n].base);
93 }
94
95 imxccm_init_clocks(sc, imxccm_init_parents);
96
97 for (int n = 0; n < size; n++) {
98 struct clk *clk = &imx6_clks[n].base;
99 struct clk *clk_parent = clk_get_parent(clk);
100 const char *parent_str = clk_parent ? clk_parent->name : "none";
101
102 aprint_verbose_dev(self, "%s (%s) : %u Hz\n", clk->name,
103 parent_str, clk_get_rate(clk));
104 }
105 }
106
107 struct clk *
108 imx6_get_clock(struct imx6ccm_softc *sc, const char *name)
109 {
110 struct imx6_clk *iclk;
111 iclk = imx6_clk_find(sc, name);
112
113 if (iclk == NULL)
114 return NULL;
115
116 return &iclk->base;
117 }
118
119 struct imx6_clk *
120 imx6_clk_find(struct imx6ccm_softc *sc, const char *name)
121 {
122 if (name == NULL)
123 return NULL;
124
125 for (int n = 0; n < sc->sc_imx6_clksize; n++) {
126 if (strcmp(sc->sc_imx6_clks[n].base.name, name) == 0)
127 return &sc->sc_imx6_clks[n];
128 }
129
130 return NULL;
131 }
132
133 static void
134 imxccm_init_clocks(struct imx6ccm_softc *sc,
135 struct imxccm_init_parent *imxccm_init_parents)
136 {
137 struct clk *clk;
138 struct clk *clk_parent;
139
140 for (u_int n = 0; imxccm_init_parents[n].clock != NULL; n++) {
141 clk = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].clock);
142 KASSERT(clk != NULL);
143 clk_parent = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].parent);
144 KASSERT(clk_parent != NULL);
145
146 int error = clk_set_parent(clk, clk_parent);
147 if (error) {
148 aprint_error_dev(sc->sc_dev,
149 "couldn't set '%s' parent to '%s': %d\n",
150 clk->name, clk_parent->name, error);
151 }
152 clk_put(clk_parent);
153 clk_put(clk);
154 }
155 }
156
157 static u_int
158 imxccm_clk_get_rate_pll_generic(struct imx6ccm_softc *sc, struct imx6_clk *iclk,
159 const u_int rate_parent)
160 {
161 struct imx6_clk_pll *pll = &iclk->clk.pll;
162 uint64_t freq = rate_parent;
163
164 KASSERT((pll->type == IMX6_CLK_PLL_GENERIC) ||
165 (pll->type == IMX6_CLK_PLL_USB));
166
167 uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg);
168 uint32_t div = __SHIFTOUT(v, pll->mask);
169
170 return freq * ((div == 1) ? 22 : 20);
171 }
172
173 static u_int
174 imxccm_clk_get_rate_pll_sys(struct imx6ccm_softc *sc, struct imx6_clk *iclk,
175 const u_int rate_parent)
176 {
177 struct imx6_clk_pll *pll = &iclk->clk.pll;
178 uint64_t freq = rate_parent;
179
180 KASSERT(pll->type == IMX6_CLK_PLL_SYS);
181
182 uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg);
183 uint32_t div = __SHIFTOUT(v, pll->mask);
184
185 return freq * div / 2;
186 }
187
188 #define PLL_AUDIO_VIDEO_NUM_OFFSET 0x10
189 #define PLL_AUDIO_VIDEO_DENOM_OFFSET 0x20
190
191 static u_int
192 imxccm_clk_get_rate_pll_audio_video(struct imx6ccm_softc *sc,
193 struct imx6_clk *iclk, const u_int rate_parent)
194 {
195 struct imx6_clk_pll *pll = &iclk->clk.pll;
196 uint64_t freq = rate_parent;
197
198 KASSERT(pll->type == IMX6_CLK_PLL_AUDIO_VIDEO);
199
200 uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg);
201 uint32_t div = __SHIFTOUT(v, pll->mask);
202 uint32_t num = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog,
203 pll->reg + PLL_AUDIO_VIDEO_NUM_OFFSET);
204 uint32_t denom = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog,
205 pll->reg + PLL_AUDIO_VIDEO_DENOM_OFFSET);
206
207 uint64_t tmp = freq * num / denom;
208
209 return freq * div + tmp;
210 }
211
212 static u_int
213 imxccm_clk_get_rate_pll_enet(struct imx6ccm_softc *sc,
214 struct imx6_clk *iclk, const u_int rate_parent)
215 {
216 struct imx6_clk_pll *pll = &iclk->clk.pll;
217
218 KASSERT(pll->type == IMX6_CLK_PLL_ENET);
219
220 return pll->ref;
221 }
222
223 static u_int
224 imxccm_clk_get_rate_fixed_factor(struct imx6ccm_softc *sc, struct imx6_clk *iclk)
225 {
226 struct imx6_clk_fixed_factor *fixed_factor = &iclk->clk.fixed_factor;
227 struct imx6_clk *parent;
228
229 KASSERT(iclk->type == IMX6_CLK_FIXED_FACTOR);
230
231 parent = imx6_clk_find(sc, iclk->parent);
232 KASSERT(parent != NULL);
233
234 uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base);
235
236 return rate_parent * fixed_factor->mult / fixed_factor->div;
237 }
238
239 static u_int
240 imxccm_clk_get_rate_pll(struct imx6ccm_softc *sc, struct imx6_clk *iclk)
241 {
242 struct imx6_clk_pll *pll = &iclk->clk.pll;
243 struct imx6_clk *parent;
244
245 KASSERT(iclk->type == IMX6_CLK_PLL);
246
247 parent = imx6_clk_find(sc, iclk->parent);
248 KASSERT(parent != NULL);
249
250 uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base);
251
252 switch(pll->type) {
253 case IMX6_CLK_PLL_GENERIC:
254 return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent);
255 case IMX6_CLK_PLL_SYS:
256 return imxccm_clk_get_rate_pll_sys(sc, iclk, rate_parent);
257 case IMX6_CLK_PLL_USB:
258 return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent);
259 case IMX6_CLK_PLL_AUDIO_VIDEO:
260 return imxccm_clk_get_rate_pll_audio_video(sc, iclk, rate_parent);
261 case IMX6_CLK_PLL_ENET:
262 return imxccm_clk_get_rate_pll_enet(sc, iclk, rate_parent);
263 default:
264 panic("imx6: unknown pll type %d", iclk->type);
265 }
266 }
267
268 static u_int
269 imxccm_clk_get_rate_div(struct imx6ccm_softc *sc, struct imx6_clk *iclk)
270 {
271 struct imx6_clk_div *div = &iclk->clk.div;
272 struct imx6_clk *parent;
273
274 KASSERT(iclk->type == IMX6_CLK_DIV);
275
276 parent = imx6_clk_find(sc, iclk->parent);
277 KASSERT(parent != NULL);
278
279 u_int rate = imxccm_clk_get_rate(sc, &parent->base);
280
281 bus_space_handle_t ioh;
282 if (div->base == IMX6_CLK_REG_CCM_ANALOG)
283 ioh = sc->sc_ioh_analog;
284 else
285 ioh = sc->sc_ioh;
286
287 uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg);
288 uint32_t n = __SHIFTOUT(v, div->mask);
289
290 if (div->type == IMX6_CLK_DIV_TABLE) {
291 KASSERT(div->tbl != NULL);
292
293 for (int i = 0; div->tbl[i] != 0; i++)
294 if (i == n)
295 rate /= div->tbl[i];
296 } else {
297 rate /= n + 1;
298 }
299
300 return rate;
301 }
302
303 static u_int
304 imxccm_clk_get_rate_pfd(struct imx6ccm_softc *sc, struct imx6_clk *iclk)
305 {
306 struct imx6_clk_pfd *pfd = &iclk->clk.pfd;
307 struct imx6_clk *parent;
308
309 KASSERT(iclk->type == IMX6_CLK_PFD);
310
311 parent = imx6_clk_find(sc, iclk->parent);
312 KASSERT(parent != NULL);
313
314 uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base);
315
316 uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pfd->reg);
317 uint32_t n = __SHIFTOUT(v, __BITS(5, 0) << (pfd->index * 8));
318
319 KASSERT(n != 0);
320
321 return (rate_parent * 18) / n;
322 }
323
324 static int
325 imxccm_clk_mux_wait(struct imx6ccm_softc *sc, struct imx6_clk_mux *mux)
326 {
327 KASSERT(mux->busy_reg == 0);
328 KASSERT(mux->busy_mask == 0);
329
330 bus_space_handle_t ioh;
331 if (mux->base == IMX6_CLK_REG_CCM_ANALOG)
332 ioh = sc->sc_ioh_analog;
333 else
334 ioh = sc->sc_ioh;
335
336 while (bus_space_read_4(sc->sc_iot, ioh, mux->busy_reg) & mux->busy_mask)
337 delay(10);
338
339 return 0;
340 }
341
342 static int
343 imxccm_clk_set_parent_mux(struct imx6ccm_softc *sc,
344 struct imx6_clk *iclk, struct clk *parent)
345 {
346 struct imx6_clk_mux *mux = &iclk->clk.mux;
347 const char *pname = parent->name;
348 u_int sel;
349
350 KASSERT(iclk->type == IMX6_CLK_MUX);
351
352 for (sel = 0; sel < mux->nparents; sel++)
353 if (strcmp(pname, mux->parents[sel]) == 0)
354 break;
355
356 if (sel == mux->nparents)
357 return EINVAL;
358
359 bus_space_handle_t ioh;
360 if (mux->base == IMX6_CLK_REG_CCM_ANALOG)
361 ioh = sc->sc_ioh_analog;
362 else
363 ioh = sc->sc_ioh;
364
365 uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg);
366 v &= ~mux->mask;
367 v |= __SHIFTIN(sel, mux->mask);
368
369 bus_space_write_4(sc->sc_iot, ioh, mux->reg, v);
370
371 iclk->parent = pname;
372
373 if (mux->type == IMX6_CLK_MUX_BUSY)
374 imxccm_clk_mux_wait(sc, mux);
375
376 return 0;
377 }
378
379 static struct imx6_clk *
380 imxccm_clk_get_parent_mux(struct imx6ccm_softc *sc, struct imx6_clk *iclk)
381 {
382 struct imx6_clk_mux *mux = &iclk->clk.mux;
383
384 KASSERT(iclk->type == IMX6_CLK_MUX);
385
386 bus_space_handle_t ioh;
387 if (mux->base == IMX6_CLK_REG_CCM_ANALOG)
388 ioh = sc->sc_ioh_analog;
389 else
390 ioh = sc->sc_ioh;
391
392 uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg);
393 u_int sel = __SHIFTOUT(v, mux->mask);
394
395 KASSERTMSG(sel < mux->nparents, "mux %s sel %d nparents %d",
396 iclk->base.name, sel, mux->nparents);
397
398 iclk->parent = mux->parents[sel];
399
400 return imx6_clk_find(sc, iclk->parent);
401 }
402
403 static int
404 imxccm_clk_set_rate_pll(struct imx6ccm_softc *sc,
405 struct imx6_clk *iclk, u_int rate)
406 {
407 /* ToDo */
408
409 return EOPNOTSUPP;
410 }
411
412 static int
413 imxccm_clk_set_rate_div(struct imx6ccm_softc *sc,
414 struct imx6_clk *iclk, u_int rate)
415 {
416 struct imx6_clk_div *div = &iclk->clk.div;
417 struct imx6_clk *parent;
418
419 KASSERT(iclk->type == IMX6_CLK_DIV);
420
421 parent = imx6_clk_find(sc, iclk->parent);
422 KASSERT(parent != NULL);
423
424 u_int rate_parent = imxccm_clk_get_rate(sc, &parent->base);
425 u_int divider = uimax(1, rate_parent / rate);
426
427 bus_space_handle_t ioh;
428 if (div->base == IMX6_CLK_REG_CCM_ANALOG)
429 ioh = sc->sc_ioh_analog;
430 else
431 ioh = sc->sc_ioh;
432
433 uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg);
434 v &= ~div->mask;
435 if (div->type == IMX6_CLK_DIV_TABLE) {
436 int n = -1;
437
438 KASSERT(div->tbl != NULL);
439 for (int i = 0; div->tbl[i] != 0; i++)
440 if (div->tbl[i] == divider)
441 n = i;
442
443 if (n >= 0)
444 v |= __SHIFTIN(n, div->mask);
445 else
446 return EINVAL;
447 } else {
448 v |= __SHIFTIN(divider - 1, div->mask);
449 }
450 bus_space_write_4(sc->sc_iot, ioh, div->reg, v);
451
452 return 0;
453 }
454
455 /*
456 * CLK Driver APIs
457 */
458 static struct clk *
459 imxccm_clk_get(void *priv, const char *name)
460 {
461 struct imx6_clk *iclk;
462 struct imx6ccm_softc *sc = priv;
463
464 iclk = imx6_clk_find(sc, name);
465 if (iclk == NULL)
466 return NULL;
467
468 atomic_inc_uint(&iclk->refcnt);
469
470 return &iclk->base;
471 }
472
473 static void
474 imxccm_clk_put(void *priv, struct clk *clk)
475 {
476 struct imx6_clk *iclk = (struct imx6_clk *)clk;
477
478 KASSERT(iclk->refcnt > 0);
479
480 atomic_dec_uint(&iclk->refcnt);
481 }
482
483 static u_int
484 imxccm_clk_get_rate(void *priv, struct clk *clk)
485 {
486 struct imx6_clk *iclk = (struct imx6_clk *)clk;
487 struct clk *parent;
488 struct imx6ccm_softc *sc = priv;
489
490 switch (iclk->type) {
491 case IMX6_CLK_FIXED:
492 return iclk->clk.fixed.rate;
493 case IMX6_CLK_FIXED_FACTOR:
494 return imxccm_clk_get_rate_fixed_factor(sc, iclk);
495 case IMX6_CLK_PLL:
496 return imxccm_clk_get_rate_pll(sc, iclk);
497 case IMX6_CLK_MUX:
498 case IMX6_CLK_GATE:
499 parent = imxccm_clk_get_parent(sc, clk);
500 return imxccm_clk_get_rate(sc, parent);
501 case IMX6_CLK_DIV:
502 return imxccm_clk_get_rate_div(sc, iclk);
503 case IMX6_CLK_PFD:
504 return imxccm_clk_get_rate_pfd(sc, iclk);
505 default:
506 panic("imx6: unknown clk type %d", iclk->type);
507 }
508 }
509
510 static int
511 imxccm_clk_set_rate(void *priv, struct clk *clk, u_int rate)
512 {
513 struct imx6_clk *iclk = (struct imx6_clk *)clk;
514 struct imx6ccm_softc *sc = priv;
515
516 switch (iclk->type) {
517 case IMX6_CLK_FIXED:
518 case IMX6_CLK_FIXED_FACTOR:
519 return ENXIO;
520 case IMX6_CLK_PLL:
521 return imxccm_clk_set_rate_pll(sc, iclk, rate);
522 case IMX6_CLK_MUX:
523 return ENXIO;
524 case IMX6_CLK_GATE:
525 return ENXIO;
526 case IMX6_CLK_DIV:
527 return imxccm_clk_set_rate_div(sc, iclk, rate);
528 case IMX6_CLK_PFD:
529 return EINVAL;
530 default:
531 panic("imx6: unknown clk type %d", iclk->type);
532 }
533 }
534
535 static int
536 imxccm_clk_enable_pll(struct imx6ccm_softc *sc, struct imx6_clk *iclk, bool enable)
537 {
538 struct imx6_clk_pll *pll = &iclk->clk.pll;
539
540 KASSERT(iclk->type == IMX6_CLK_PLL);
541
542 /* Power up bit */
543 if (pll->type == IMX6_CLK_PLL_USB)
544 enable = !enable;
545
546 bus_space_handle_t ioh = sc->sc_ioh_analog;
547 uint32_t v = bus_space_read_4(sc->sc_iot, ioh, pll->reg);
548 if (__SHIFTOUT(v, pll->powerdown) != enable)
549 return 0;
550 if (enable)
551 v &= ~pll->powerdown;
552 else
553 v |= pll->powerdown;
554 bus_space_write_4(sc->sc_iot, ioh, pll->reg, v);
555
556 /* wait look */
557 while (!(bus_space_read_4(sc->sc_iot, ioh, pll->reg) & CCM_ANALOG_PLL_LOCK))
558 delay(10);
559
560 return 0;
561 }
562
563 static int
564 imxccm_clk_enable_gate(struct imx6ccm_softc *sc, struct imx6_clk *iclk, bool enable)
565 {
566 struct imx6_clk_gate *gate = &iclk->clk.gate;
567
568 KASSERT(iclk->type == IMX6_CLK_GATE);
569
570 bus_space_handle_t ioh;
571 if (gate->base == IMX6_CLK_REG_CCM_ANALOG)
572 ioh = sc->sc_ioh_analog;
573 else
574 ioh = sc->sc_ioh;
575
576 uint32_t v = bus_space_read_4(sc->sc_iot, ioh, gate->reg);
577 if (enable) {
578 if (gate->exclusive_mask)
579 v &= ~gate->exclusive_mask;
580 v |= gate->mask;
581 } else {
582 if (gate->exclusive_mask)
583 v |= gate->exclusive_mask;
584 v &= ~gate->mask;
585 }
586 bus_space_write_4(sc->sc_iot, ioh, gate->reg, v);
587
588 return 0;
589 }
590
591 static int
592 imxccm_clk_enable(void *priv, struct clk *clk)
593 {
594 struct imx6_clk *iclk = (struct imx6_clk *)clk;
595 struct imx6_clk *parent = NULL;
596 struct imx6ccm_softc *sc = priv;
597
598 if ((parent = imx6_clk_find(sc, iclk->parent)) != NULL)
599 imxccm_clk_enable(sc, &parent->base);
600
601 switch (iclk->type) {
602 case IMX6_CLK_FIXED:
603 case IMX6_CLK_FIXED_FACTOR:
604 return 0; /* always on */
605 case IMX6_CLK_PLL:
606 return imxccm_clk_enable_pll(sc, iclk, true);
607 case IMX6_CLK_MUX:
608 case IMX6_CLK_DIV:
609 case IMX6_CLK_PFD:
610 return 0;
611 case IMX6_CLK_GATE:
612 return imxccm_clk_enable_gate(sc, iclk, true);
613 default:
614 panic("imx6: unknown clk type %d", iclk->type);
615 }
616 }
617
618 static int
619 imxccm_clk_disable(void *priv, struct clk *clk)
620 {
621 struct imx6_clk *iclk = (struct imx6_clk *)clk;
622 struct imx6ccm_softc *sc = priv;
623
624 switch (iclk->type) {
625 case IMX6_CLK_FIXED:
626 case IMX6_CLK_FIXED_FACTOR:
627 return EINVAL; /* always on */
628 case IMX6_CLK_PLL:
629 return imxccm_clk_enable_pll(sc, iclk, false);
630 case IMX6_CLK_MUX:
631 case IMX6_CLK_DIV:
632 case IMX6_CLK_PFD:
633 return EINVAL;
634 case IMX6_CLK_GATE:
635 return imxccm_clk_enable_gate(sc, iclk, false);
636 default:
637 panic("imx6: unknown clk type %d", iclk->type);
638 }
639 }
640
641 static int
642 imxccm_clk_set_parent(void *priv, struct clk *clk, struct clk *parent)
643 {
644 struct imx6_clk *iclk = (struct imx6_clk *)clk;
645 struct imx6ccm_softc *sc = priv;
646
647 switch (iclk->type) {
648 case IMX6_CLK_FIXED:
649 case IMX6_CLK_FIXED_FACTOR:
650 case IMX6_CLK_PLL:
651 case IMX6_CLK_GATE:
652 case IMX6_CLK_DIV:
653 case IMX6_CLK_PFD:
654 return EINVAL;
655 case IMX6_CLK_MUX:
656 return imxccm_clk_set_parent_mux(sc, iclk, parent);
657 default:
658 panic("imx6: unknown clk type %d", iclk->type);
659 }
660 }
661
662 static struct clk *
663 imxccm_clk_get_parent(void *priv, struct clk *clk)
664 {
665 struct imx6_clk *iclk = (struct imx6_clk *)clk;
666 struct imx6_clk *parent = NULL;
667 struct imx6ccm_softc *sc = priv;
668
669 switch (iclk->type) {
670 case IMX6_CLK_FIXED:
671 case IMX6_CLK_FIXED_FACTOR:
672 case IMX6_CLK_PLL:
673 case IMX6_CLK_GATE:
674 case IMX6_CLK_DIV:
675 case IMX6_CLK_PFD:
676 if (iclk->parent != NULL)
677 parent = imx6_clk_find(sc, iclk->parent);
678 break;
679 case IMX6_CLK_MUX:
680 parent = imxccm_clk_get_parent_mux(sc, iclk);
681 break;
682 default:
683 panic("imx6: unknown clk type %d", iclk->type);
684 }
685
686 return (struct clk *)parent;
687 }
688