1 /*-
2 * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/lock.h>
33 #include <sys/mutex.h>
34 #include <sys/rman.h>
35 #include <machine/bus.h>
36
37 #include <dev/extres/clk/clk.h>
38 #include <dev/extres/clk/clk_div.h>
39 #include <dev/extres/clk/clk_fixed.h>
40 #include <dev/extres/clk/clk_mux.h>
41
42 #include "qcom_clk_freqtbl.h"
43 #include "qcom_clk_rcg2.h"
44 #include "qcom_clk_rcg2_reg.h"
45
46 #include "clkdev_if.h"
47
48 #if 0
49 #define DPRINTF(dev, msg...) device_printf(dev, msg);
50 #else
51 #define DPRINTF(dev, msg...)
52 #endif
53
54 #define QCOM_CLK_RCG2_CFG_OFFSET(sc) \
55 ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_CFG_REG)
56 #define QCOM_CLK_RCG2_CMD_REGISTER(sc) \
57 ((sc)->cmd_rcgr + QCOM_CLK_RCG2_CMD_REG)
58 #define QCOM_CLK_RCG2_M_OFFSET(sc) \
59 ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_M_REG)
60 #define QCOM_CLK_RCG2_N_OFFSET(sc) \
61 ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_N_REG)
62 #define QCOM_CLK_RCG2_D_OFFSET(sc) \
63 ((sc)->cmd_rcgr + (sc)->cfg_offset + QCOM_CLK_RCG2_D_REG)
64
65 struct qcom_clk_rcg2_sc {
66 struct clknode *clknode;
67 uint32_t cmd_rcgr;
68 uint32_t hid_width;
69 uint32_t mnd_width;
70 int32_t safe_src_idx;
71 uint32_t cfg_offset;
72 int safe_pre_parent_idx;
73 uint32_t flags;
74 const struct qcom_clk_freq_tbl *freq_tbl;
75 };
76
77
78 /*
79 * Finish a clock update.
80 *
81 * This instructs the configuration to take effect.
82 */
83 static bool
84 qcom_clk_rcg2_update_config_locked(struct qcom_clk_rcg2_sc *sc)
85 {
86 uint32_t reg, count;
87
88 /*
89 * Send "update" to the controller.
90 */
91 CLKDEV_READ_4(clknode_get_device(sc->clknode),
92 QCOM_CLK_RCG2_CMD_REGISTER(sc), ®);
93 reg |= QCOM_CLK_RCG2_CMD_UPDATE;
94 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
95 QCOM_CLK_RCG2_CMD_REGISTER(sc), reg);
96 wmb();
97
98 /*
99 * Poll for completion of update.
100 */
101 for (count = 0; count < 1000; count++) {
102 CLKDEV_READ_4(clknode_get_device(sc->clknode),
103 QCOM_CLK_RCG2_CMD_REGISTER(sc), ®);
104 if ((reg & QCOM_CLK_RCG2_CMD_UPDATE) == 0) {
105 return (true);
106 }
107 DELAY(10);
108 rmb();
109 }
110
111 CLKDEV_READ_4(clknode_get_device(sc->clknode),
112 QCOM_CLK_RCG2_CMD_REGISTER(sc), ®);
113 DPRINTF(clknode_get_device(sc->clknode), "%s: failed; reg=0x%08x\n",
114 __func__, reg);
115 return (false);
116 }
117
118 /*
119 * Calculate the output frequency given an input frequency and the m/n:d
120 * configuration.
121 */
122 static uint64_t
123 qcom_clk_rcg2_calc_rate(uint64_t rate, uint32_t mode, uint32_t m, uint32_t n,
124 uint32_t hid_div)
125 {
126 if (hid_div != 0) {
127 rate = rate * 2;
128 rate = rate / (hid_div + 1);
129 }
130
131 /* Note: assume n is not 0 here; bad things happen if it is */
132
133 if (mode != 0) {
134 rate = (rate * m) / n;
135 }
136
137 return (rate);
138 }
139
140 /*
141 * The inverse of calc_rate() - calculate the required input frequency
142 * given the desired output freqency and m/n:d configuration.
143 */
144 static uint64_t
145 qcom_clk_rcg2_calc_input_freq(uint64_t freq, uint32_t m, uint32_t n,
146 uint32_t hid_div)
147 {
148 if (hid_div != 0) {
149 freq = freq / 2;
150 freq = freq * (hid_div + 1);
151 }
152
153 if (n != 0) {
154 freq = (freq * n) / m;
155 }
156
157 return (freq);
158 }
159
160 static int
161 qcom_clk_rcg2_recalc(struct clknode *clk, uint64_t *freq)
162 {
163 struct qcom_clk_rcg2_sc *sc;
164 uint32_t cfg, m = 0, n = 0, hid_div = 0;
165 uint32_t mode = 0, mask;
166
167 sc = clknode_get_softc(clk);
168
169 /* Read the MODE, CFG, M and N parameters */
170 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
171 CLKDEV_READ_4(clknode_get_device(sc->clknode),
172 QCOM_CLK_RCG2_CFG_OFFSET(sc),
173 &cfg);
174 if (sc->mnd_width != 0) {
175 mask = (1U << sc->mnd_width) - 1;
176 CLKDEV_READ_4(clknode_get_device(sc->clknode),
177 QCOM_CLK_RCG2_M_OFFSET(sc), &m);
178 CLKDEV_READ_4(clknode_get_device(sc->clknode),
179 QCOM_CLK_RCG2_N_OFFSET(sc), &n);
180 m = m & mask;
181 n = ~ n;
182 n = n & mask;
183 n = n + m;
184 mode = (cfg & QCOM_CLK_RCG2_CFG_MODE_MASK)
185 >> QCOM_CLK_RCG2_CFG_MODE_SHIFT;
186 }
187 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
188
189 /* Fetch the divisor */
190 mask = (1U << sc->hid_width) - 1;
191 hid_div = (cfg >> QCOM_CLK_RCG2_CFG_SRC_DIV_SHIFT) & mask;
192
193 /* Calculate the rate based on the parent rate and config */
194 *freq = qcom_clk_rcg2_calc_rate(*freq, mode, m, n, hid_div);
195
196 return (0);
197 }
198
199 /*
200 * configure the mn:d divisor, pre-divisor, and parent.
201 */
202 static void
203 qcom_clk_rcg2_set_config_locked(struct qcom_clk_rcg2_sc *sc,
204 const struct qcom_clk_freq_tbl *f, int parent_idx)
205 {
206 uint32_t mask, reg;
207
208 /* If we have MN:D, then update it */
209 if (sc->mnd_width != 0 && f->n != 0) {
210 mask = (1U << sc->mnd_width) - 1;
211
212 CLKDEV_READ_4(clknode_get_device(sc->clknode),
213 QCOM_CLK_RCG2_M_OFFSET(sc), ®);
214 reg &= ~mask;
215 reg |= (f->m & mask);
216 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
217 QCOM_CLK_RCG2_M_OFFSET(sc), reg);
218
219 CLKDEV_READ_4(clknode_get_device(sc->clknode),
220 QCOM_CLK_RCG2_N_OFFSET(sc), ®);
221 reg &= ~mask;
222 reg |= ((~(f->n - f->m)) & mask);
223 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
224 QCOM_CLK_RCG2_N_OFFSET(sc), reg);
225
226 CLKDEV_READ_4(clknode_get_device(sc->clknode),
227 QCOM_CLK_RCG2_D_OFFSET(sc), ®);
228 reg &= ~mask;
229 reg |= ((~f->n) & mask);
230 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
231 QCOM_CLK_RCG2_D_OFFSET(sc), reg);
232 }
233
234 mask = (1U << sc->hid_width) - 1;
235 /*
236 * Mask out register fields we're going to modify along with
237 * the pre-divisor.
238 */
239 mask |= QCOM_CLK_RCG2_CFG_SRC_SEL_MASK
240 | QCOM_CLK_RCG2_CFG_MODE_MASK
241 | QCOM_CLK_RCG2_CFG_HW_CLK_CTRL_MASK;
242
243 CLKDEV_READ_4(clknode_get_device(sc->clknode),
244 QCOM_CLK_RCG2_CFG_OFFSET(sc), ®);
245 reg &= ~mask;
246
247 /* Configure pre-divisor */
248 reg = reg | ((f->pre_div) << QCOM_CLK_RCG2_CFG_SRC_DIV_SHIFT);
249
250 /* Configure parent clock */
251 reg = reg | (((parent_idx << QCOM_CLK_RCG2_CFG_SRC_SEL_SHIFT)
252 & QCOM_CLK_RCG2_CFG_SRC_SEL_MASK));
253
254 /* Configure dual-edge if needed */
255 if (sc->mnd_width != 0 && f->n != 0 && (f->m != f->n))
256 reg |= QCOM_CLK_RCG2_CFG_MODE_DUAL_EDGE;
257
258 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
259 QCOM_CLK_RCG2_CFG_OFFSET(sc), reg);
260 }
261
262 static int
263 qcom_clk_rcg2_init(struct clknode *clk, device_t dev)
264 {
265 struct qcom_clk_rcg2_sc *sc;
266 uint32_t reg;
267 uint32_t idx;
268 bool enabled __unused;
269
270 sc = clknode_get_softc(clk);
271
272 /*
273 * Read the mux setting to set the right parent.
274 * Whilst here, read the config to get whether we're enabled
275 * or not.
276 */
277 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
278 /* check if rcg2 root clock is enabled */
279 CLKDEV_READ_4(clknode_get_device(sc->clknode),
280 QCOM_CLK_RCG2_CMD_REGISTER(sc), ®);
281 if (reg & QCOM_CLK_RCG2_CMD_ROOT_OFF)
282 enabled = false;
283 else
284 enabled = true;
285
286 /* mux settings */
287 CLKDEV_READ_4(clknode_get_device(sc->clknode),
288 QCOM_CLK_RCG2_CFG_OFFSET(sc), ®);
289 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
290
291 idx = (reg & QCOM_CLK_RCG2_CFG_SRC_SEL_MASK)
292 >> QCOM_CLK_RCG2_CFG_SRC_SEL_SHIFT;
293 DPRINTF(clknode_get_device(sc->clknode),
294 "%s: mux index %u, enabled=%d\n",
295 __func__, idx, enabled);
296 clknode_init_parent_idx(clk, idx);
297
298 /*
299 * If we could be sure our parent clocks existed here in the tree,
300 * we could calculate our current frequency by fetching the parent
301 * frequency and then do our divider math. Unfortunately that
302 * currently isn't the case.
303 */
304
305 return(0);
306 }
307
308 static int
309 qcom_clk_rcg2_set_gate(struct clknode *clk, bool enable)
310 {
311
312 /*
313 * For now this isn't supported; there's some support for
314 * "shared" rcg2 nodes in the Qualcomm/upstream Linux trees but
315 * it's not currently needed for the supported platforms.
316 */
317 return (0);
318 }
319
320 /*
321 * Program the parent index.
322 *
323 * This doesn't do the update. It also must be called with the device
324 * lock held.
325 */
326 static void
327 qcom_clk_rcg2_set_parent_index_locked(struct qcom_clk_rcg2_sc *sc,
328 uint32_t index)
329 {
330 uint32_t reg;
331
332 CLKDEV_READ_4(clknode_get_device(sc->clknode),
333 QCOM_CLK_RCG2_CFG_OFFSET(sc), ®);
334 reg = reg & ~QCOM_CLK_RCG2_CFG_SRC_SEL_MASK;
335 reg = reg | (((index << QCOM_CLK_RCG2_CFG_SRC_SEL_SHIFT)
336 & QCOM_CLK_RCG2_CFG_SRC_SEL_MASK));
337 CLKDEV_WRITE_4(clknode_get_device(sc->clknode),
338 QCOM_CLK_RCG2_CFG_OFFSET(sc),
339 reg);
340 }
341
342 /*
343 * Set frequency
344 *
345 * fin - the parent frequency, if exists
346 * fout - starts as the requested frequency, ends with the configured
347 * or dry-run frequency
348 * Flags - CLK_SET_DRYRUN, CLK_SET_ROUND_UP, CLK_SET_ROUND_DOWN
349 * retval - 0, ERANGE
350 */
351 static int
352 qcom_clk_rcg2_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
353 int flags, int *stop)
354 {
355 struct qcom_clk_rcg2_sc *sc;
356 const struct qcom_clk_freq_tbl *f;
357 const char **parent_names;
358 uint64_t p_freq, p_clk_freq;
359 int parent_cnt;
360 struct clknode *p_clk;
361 int i;
362
363 sc = clknode_get_softc(clk);
364
365 /*
366 * Find a suitable frequency in the frequency table.
367 *
368 * TODO: should pay attention to ROUND_UP / ROUND_DOWN and add
369 * a freqtbl method to handle both accordingly.
370 */
371 f = qcom_clk_freq_tbl_lookup(sc->freq_tbl, *fout);
372 if (f == NULL) {
373 device_printf(clknode_get_device(sc->clknode),
374 "%s: no suitable freqtbl entry found for freq %llu\n",
375 __func__,
376 *fout);
377 return (ERANGE);
378 }
379
380 /*
381 * Find the parent index for the given parent clock.
382 * Abort if we can't actually find it.
383 *
384 * XXX TODO: this should be a clk API call!
385 */
386 parent_cnt = clknode_get_parents_num(clk);
387 parent_names = clknode_get_parent_names(clk);
388 for (i = 0; i < parent_cnt; i++) {
389 if (parent_names[i] == NULL)
390 continue;
391 if (strcmp(parent_names[i], f->parent) == 0)
392 break;
393 }
394 if (i >= parent_cnt) {
395 device_printf(clknode_get_device(sc->clknode),
396 "%s: couldn't find suitable parent?\n",
397 __func__);
398 return (ENXIO);
399 }
400
401 /*
402 * If we aren't setting the parent clock, then we need
403 * to just program the new parent clock in and update.
404 * (or for DRYRUN just skip that and return the new
405 * frequency.)
406 */
407 if ((sc->flags & QCOM_CLK_RCG2_FLAGS_SET_RATE_PARENT) == 0) {
408 if (flags & CLK_SET_DRYRUN) {
409 *fout = f->freq;
410 return (0);
411 }
412
413 if (sc->safe_pre_parent_idx > -1) {
414 DPRINTF(clknode_get_device(sc->clknode),
415 "%s: setting to safe parent idx %d\n",
416 __func__,
417 sc->safe_pre_parent_idx);
418 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
419 qcom_clk_rcg2_set_parent_index_locked(sc,
420 sc->safe_pre_parent_idx);
421 DPRINTF(clknode_get_device(sc->clknode),
422 "%s: safe parent: updating config\n", __func__);
423 if (! qcom_clk_rcg2_update_config_locked(sc)) {
424 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
425 DPRINTF(clknode_get_device(sc->clknode),
426 "%s: error updating config\n",
427 __func__);
428 return (ENXIO);
429 }
430 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
431 DPRINTF(clknode_get_device(sc->clknode),
432 "%s: safe parent: done\n", __func__);
433 clknode_set_parent_by_idx(sc->clknode,
434 sc->safe_pre_parent_idx);
435 }
436 /* Program parent index, then schedule update */
437 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
438 qcom_clk_rcg2_set_parent_index_locked(sc, i);
439 if (! qcom_clk_rcg2_update_config_locked(sc)) {
440 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
441 device_printf(clknode_get_device(sc->clknode),
442 "%s: couldn't program in parent idx %u!\n",
443 __func__, i);
444 return (ENXIO);
445 }
446 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
447 clknode_set_parent_by_idx(sc->clknode, i);
448 *fout = f->freq;
449 return (0);
450 }
451
452 /*
453 * If we /are/ setting the parent clock, then we need
454 * to determine what frequency we need the parent to
455 * be, and then reconfigure the parent to the new
456 * frequency, and then change our parent.
457 *
458 * (Again, if we're doing DRYRUN, just skip that
459 * and return the new frequency.)
460 */
461 p_clk = clknode_find_by_name(f->parent);
462 if (p_clk == NULL) {
463 device_printf(clknode_get_device(sc->clknode),
464 "%s: couldn't find parent clk (%s)\n",
465 __func__, f->parent);
466 return (ENXIO);
467 }
468
469 /*
470 * Calculate required frequency from said parent clock to
471 * meet the needs of our target clock.
472 */
473 p_freq = qcom_clk_rcg2_calc_input_freq(f->freq, f->m, f->n,
474 f->pre_div);
475 DPRINTF(clknode_get_device(sc->clknode),
476 "%s: request %llu, parent %s freq %llu, parent freq %llu\n",
477 __func__,
478 *fout,
479 f->parent,
480 f->freq,
481 p_freq);
482
483 /*
484 * To ensure glitch-free operation on some clocks, set it to
485 * a safe parent before programming our divisor and the parent
486 * clock configuration. Then once it's done, flip the parent
487 * to the new parent.
488 *
489 * If we're doing a dry-run then we don't need to re-parent the
490 * clock just yet!
491 */
492 if (((flags & CLK_SET_DRYRUN) == 0) &&
493 (sc->safe_pre_parent_idx > -1)) {
494 DPRINTF(clknode_get_device(sc->clknode),
495 "%s: setting to safe parent idx %d\n",
496 __func__,
497 sc->safe_pre_parent_idx);
498 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
499 qcom_clk_rcg2_set_parent_index_locked(sc,
500 sc->safe_pre_parent_idx);
501 DPRINTF(clknode_get_device(sc->clknode),
502 "%s: safe parent: updating config\n", __func__);
503 if (! qcom_clk_rcg2_update_config_locked(sc)) {
504 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
505 DPRINTF(clknode_get_device(sc->clknode),
506 "%s: error updating config\n",
507 __func__);
508 return (ENXIO);
509 }
510 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
511 DPRINTF(clknode_get_device(sc->clknode),
512 "%s: safe parent: done\n", __func__);
513 clknode_set_parent_by_idx(sc->clknode,
514 sc->safe_pre_parent_idx);
515 }
516
517 /*
518 * Set the parent frequency before we change our mux and divisor
519 * configuration.
520 */
521 if (clknode_get_freq(p_clk, &p_clk_freq) != 0) {
522 device_printf(clknode_get_device(sc->clknode),
523 "%s: couldn't get freq for parent clock %s\n",
524 __func__,
525 f->parent);
526 return (ENXIO);
527 }
528 if (p_clk_freq != p_freq) {
529 uint64_t n_freq;
530 int rv;
531
532 /*
533 * If we're doing a dryrun then call test_freq() not set_freq().
534 * That way we get the frequency back that we would be set to.
535 *
536 * If we're not doing a dry run then set the frequency, then
537 * call get_freq to get what it was set to.
538 */
539 if (flags & CLK_SET_DRYRUN) {
540 n_freq = p_freq;
541 rv = clknode_test_freq(p_clk, n_freq, flags, 0,
542 &p_freq);
543 } else {
544 rv = clknode_set_freq(p_clk, p_freq, flags, 0);
545 }
546
547 if (rv != 0) {
548 device_printf(clknode_get_device(sc->clknode),
549 "%s: couldn't set parent clock %s frequency to "
550 "%llu\n",
551 __func__,
552 f->parent,
553 p_freq);
554 return (ENXIO);
555 }
556
557 /* Frequency was set, fetch what it was set to */
558 if ((flags & CLK_SET_DRYRUN) == 0) {
559 rv = clknode_get_freq(p_clk, &p_freq);
560 if (rv != 0) {
561 device_printf(clknode_get_device(sc->clknode),
562 "%s: couldn't get parent frequency",
563 __func__);
564 return (ENXIO);
565 }
566 }
567 }
568
569 DPRINTF(clknode_get_device(sc->clknode),
570 "%s: requested freq=%llu, target freq=%llu,"
571 " parent choice=%s, parent_freq=%llu\n",
572 __func__,
573 *fout,
574 f->freq,
575 f->parent,
576 p_freq);
577
578 /*
579 * Set the parent node, the parent programming and the divisor
580 * config. Because they're done together, we don't go via
581 * a mux method on this node.
582 */
583
584 /*
585 * Program the divisor and parent.
586 */
587 if ((flags & CLK_SET_DRYRUN) == 0) {
588 CLKDEV_DEVICE_LOCK(clknode_get_device(sc->clknode));
589 qcom_clk_rcg2_set_config_locked(sc, f, i);
590 if (! qcom_clk_rcg2_update_config_locked(sc)) {
591 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
592 device_printf(clknode_get_device(sc->clknode),
593 "%s: couldn't program in divisor, help!\n",
594 __func__);
595 return (ENXIO);
596 }
597 CLKDEV_DEVICE_UNLOCK(clknode_get_device(sc->clknode));
598 clknode_set_parent_by_idx(sc->clknode, i);
599 }
600
601 /*
602 * p_freq is now the frequency that the parent /is/ set to.
603 * (Or would be set to for a dry run.)
604 *
605 * Calculate what the eventual frequency would be, we'll want
606 * this to return when we're done - and again, if it's a dryrun,
607 * don't set anything up. This doesn't rely on the register
608 * contents.
609 */
610 *fout = qcom_clk_rcg2_calc_rate(p_freq, (f->n == 0 ? 0 : 1),
611 f->m, f->n, f->pre_div);
612
613 return (0);
614 }
615
616 static clknode_method_t qcom_clk_rcg2_methods[] = {
617 /* Device interface */
618 CLKNODEMETHOD(clknode_init, qcom_clk_rcg2_init),
619 CLKNODEMETHOD(clknode_recalc_freq, qcom_clk_rcg2_recalc),
620 CLKNODEMETHOD(clknode_set_gate, qcom_clk_rcg2_set_gate),
621 CLKNODEMETHOD(clknode_set_freq, qcom_clk_rcg2_set_freq),
622 CLKNODEMETHOD_END
623 };
624
625 DEFINE_CLASS_1(qcom_clk_fepll, qcom_clk_rcg2_class, qcom_clk_rcg2_methods,
626 sizeof(struct qcom_clk_rcg2_sc), clknode_class);
627
628 int
629 qcom_clk_rcg2_register(struct clkdom *clkdom,
630 struct qcom_clk_rcg2_def *clkdef)
631 {
632 struct clknode *clk;
633 struct qcom_clk_rcg2_sc *sc;
634
635 /*
636 * Right now the rcg2 code isn't supporting turning off the clock
637 * or limiting it to the lowest parent clock. But, do set the
638 * flags appropriately.
639 */
640 if (clkdef->flags & QCOM_CLK_RCG2_FLAGS_CRITICAL)
641 clkdef->clkdef.flags |= CLK_NODE_CANNOT_STOP;
642
643 clk = clknode_create(clkdom, &qcom_clk_rcg2_class, &clkdef->clkdef);
644 if (clk == NULL)
645 return (1);
646
647 sc = clknode_get_softc(clk);
648 sc->clknode = clk;
649
650 sc->cmd_rcgr = clkdef->cmd_rcgr;
651 sc->hid_width = clkdef->hid_width;
652 sc->mnd_width = clkdef->mnd_width;
653 sc->safe_src_idx = clkdef->safe_src_idx;
654 sc->safe_pre_parent_idx = clkdef->safe_pre_parent_idx;
655 sc->cfg_offset = clkdef->cfg_offset;
656 sc->flags = clkdef->flags;
657 sc->freq_tbl = clkdef->freq_tbl;
658
659 clknode_register(clkdom, clk);
660
661 return (0);
662 }
Cache object: a9ccafbe2fe7ec63ba9bdec6040b00e9
|