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