]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/qcom_clk/qcom_clk_rcg2.c
unbound: Vendor import 1.17.0
[FreeBSD/FreeBSD.git] / sys / dev / qcom_clk / qcom_clk_rcg2.c
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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), &reg);
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: requsted 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 }