2 * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * Ingenic JZ4780 generic CGU clock driver.
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/systm.h>
40 #include <sys/mutex.h>
41 #include <sys/resource.h>
43 #include <machine/bus.h>
45 #include <mips/ingenic/jz4780_clk.h>
46 #include <mips/ingenic/jz4780_regs.h>
48 /* JZ4780 generic mux and div clocks implementation */
49 static int jz4780_clk_gen_init(struct clknode *clk, device_t dev);
50 static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq);
51 static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
52 uint64_t *fout, int flags, int *stop);
53 static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable);
54 static int jz4780_clk_gen_set_mux(struct clknode *clk, int src);
56 struct jz4780_clk_gen_sc {
58 struct resource *clk_res;
60 const struct jz4780_clk_descr *clk_descr;
64 * JZ4780 clock PLL clock methods
66 static clknode_method_t jz4780_clk_gen_methods[] = {
67 CLKNODEMETHOD(clknode_init, jz4780_clk_gen_init),
68 CLKNODEMETHOD(clknode_set_gate, jz4780_clk_gen_set_gate),
69 CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_gen_recalc_freq),
70 CLKNODEMETHOD(clknode_set_freq, jz4780_clk_gen_set_freq),
71 CLKNODEMETHOD(clknode_set_mux, jz4780_clk_gen_set_mux),
75 DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods,
76 sizeof(struct jz4780_clk_gen_sc), clknode_class);
78 static inline unsigned
79 mux_to_reg(unsigned src, unsigned map)
84 for (ret = 0; bit; ret++, bit >>= 1) {
93 static inline unsigned
94 reg_to_mux(unsigned reg, unsigned map)
99 for (ret = 0; reg; reg--, bit >>= 1)
106 jz4780_clk_gen_init(struct clknode *clk, device_t dev)
108 struct jz4780_clk_gen_sc *sc;
109 uint32_t reg, msk, parent_idx;
111 sc = clknode_get_softc(clk);
113 /* Figure our parent out */
114 if (sc->clk_descr->clk_type & CLK_MASK_MUX) {
115 msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
116 reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
117 reg = (reg >> sc->clk_descr->clk_mux.mux_shift) & msk;
118 parent_idx = reg_to_mux(reg, sc->clk_descr->clk_mux.mux_map);
123 clknode_init_parent_idx(clk, parent_idx);
128 jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq)
130 struct jz4780_clk_gen_sc *sc;
133 sc = clknode_get_softc(clk);
135 /* Calculate divisor frequency */
136 if (sc->clk_descr->clk_type & CLK_MASK_DIV) {
139 msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
140 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
141 reg = (reg >> sc->clk_descr->clk_div.div_shift) & msk;
142 reg = (reg + 1) << sc->clk_descr->clk_div.div_lg;
148 #define DIV_TIMEOUT 100
151 jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
152 uint64_t *fout, int flags, int *stop)
154 struct jz4780_clk_gen_sc *sc;
156 uint32_t divider, div_reg, div_msk, reg, div_l, div_h;
159 sc = clknode_get_softc(clk);
161 /* Find closest divider */
162 div_l = howmany(fin, *fout);
164 divider = abs((int64_t)*fout - (fin / div_l)) <
165 abs((int64_t)*fout - (fin / div_h)) ? div_l : div_h;
167 /* Adjust for divider multiplier */
168 div_reg = divider >> sc->clk_descr->clk_div.div_lg;
169 divider = div_reg << sc->clk_descr->clk_div.div_lg;
173 _fout = fin / divider;
176 if ((flags & CLK_SET_ROUND_UP) && (*fout > _fout))
178 else if ((flags & CLK_SET_ROUND_DOWN) && (*fout < _fout))
183 div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
186 if (div_reg > div_msk + 1) {
191 divider = (div_reg << sc->clk_descr->clk_div.div_lg);
194 if ((flags & CLK_SET_DRYRUN) != 0) {
195 if (*stop != 0 && *fout != fin / divider &&
196 (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0)
198 *fout = fin / divider;
203 /* Apply the new divider value */
204 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
205 reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift);
206 reg |= (div_reg << sc->clk_descr->clk_div.div_shift);
207 /* Set the change enable bit, it present */
208 if (sc->clk_descr->clk_div.div_ce_bit >= 0)
209 reg |= (1u << sc->clk_descr->clk_div.div_ce_bit);
210 /* Clear stop bit, it present */
211 if (sc->clk_descr->clk_div.div_st_bit >= 0)
212 reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit);
213 /* Initiate the change */
214 CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg);
216 /* Wait for busy bit to clear indicating the change is complete */
218 if (sc->clk_descr->clk_div.div_busy_bit >= 0) {
221 for (i = 0; i < DIV_TIMEOUT; i++) {
222 reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
223 if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit)))
227 if (i == DIV_TIMEOUT)
232 *fout = fin / divider;
237 jz4780_clk_gen_set_mux(struct clknode *clk, int src)
239 struct jz4780_clk_gen_sc *sc;
242 sc = clknode_get_softc(clk);
244 /* Only mux nodes are capable of being reparented */
245 if (!(sc->clk_descr->clk_type & CLK_MASK_MUX))
246 return (src ? EINVAL : 0);
248 msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
249 src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map);
252 reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
253 reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift);
254 reg |= (src << sc->clk_descr->clk_mux.mux_shift);
255 CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg);
262 jz4780_clk_gen_set_gate(struct clknode *clk, bool enable)
264 struct jz4780_clk_gen_sc *sc;
265 uint32_t off, reg, bit;
267 sc = clknode_get_softc(clk);
269 /* Check is clock can be gated */
270 if (sc->clk_descr->clk_gate_bit < 0)
273 bit = sc->clk_descr->clk_gate_bit;
282 reg = CLK_RD_4(sc, off);
287 CLK_WR_4(sc, off, reg);
294 int jz4780_clk_gen_register(struct clkdom *clkdom,
295 const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
296 struct resource *mem_res)
298 struct clknode_init_def clkdef;
300 struct jz4780_clk_gen_sc *sc;
302 clkdef.id = descr->clk_id;
303 clkdef.name = __DECONST(char *, descr->clk_name);
304 /* Silly const games to work around API deficiency */
305 clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0];
306 clkdef.flags = CLK_NODE_STATIC_STRINGS;
307 if (descr->clk_type & CLK_MASK_MUX)
308 clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map);
310 clkdef.parent_cnt = 1;
312 clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef);
316 sc = clknode_get_softc(clk);
317 sc->clk_mtx = dev_mtx;
318 sc->clk_res = mem_res;
319 sc->clk_descr = descr;
320 clknode_register(clkdom, clk);