1 /*-
2  * Copyright 2015 Alexander Kabaev <[email protected]>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * Ingenic JZ4780 generic CGU clock driver.
29  *
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/bus.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/resource.h>
42 
43 #include <machine/bus.h>
44 
45 #include <mips/ingenic/jz4780_clk.h>
46 #include <mips/ingenic/jz4780_regs.h>
47 
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);
55 
56 struct jz4780_clk_gen_sc {
57 	struct mtx	*clk_mtx;
58 	struct resource *clk_res;
59 	int clk_reg;
60 	const struct jz4780_clk_descr *clk_descr;
61 };
62 
63 /*
64  * JZ4780 clock PLL clock methods
65  */
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),
72 
73 	CLKNODEMETHOD_END
74 };
75 DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods,
76        sizeof(struct jz4780_clk_gen_sc), clknode_class);
77 
78 static inline unsigned
mux_to_reg(unsigned src,unsigned map)79 mux_to_reg(unsigned src, unsigned map)
80 {
81 	unsigned ret, bit;
82 
83 	bit = (1u << 3);
84 	for (ret = 0; bit; ret++, bit >>= 1) {
85 		if (map & bit) {
86 			if (src-- == 0)
87 				return (ret);
88 		}
89 	}
90 	panic("mux_to_reg");
91 }
92 
93 static inline unsigned
reg_to_mux(unsigned reg,unsigned map)94 reg_to_mux(unsigned reg, unsigned map)
95 {
96 	unsigned ret, bit;
97 
98 	bit = (1u << 3);
99 	for (ret = 0; reg; reg--, bit >>= 1)
100 		if (map & bit)
101 			ret++;
102 	return (ret);
103 }
104 
105 static int
jz4780_clk_gen_init(struct clknode * clk,device_t dev)106 jz4780_clk_gen_init(struct clknode *clk, device_t dev)
107 {
108 	struct jz4780_clk_gen_sc *sc;
109 	uint32_t reg, msk, parent_idx;
110 
111 	sc = clknode_get_softc(clk);
112 	CLK_LOCK(sc);
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);
119 	} else
120 		parent_idx = 0;
121 	CLK_UNLOCK(sc);
122 
123 	clknode_init_parent_idx(clk, parent_idx);
124 	return (0);
125 }
126 
127 static int
jz4780_clk_gen_recalc_freq(struct clknode * clk,uint64_t * freq)128 jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq)
129 {
130 	struct jz4780_clk_gen_sc *sc;
131 	uint32_t reg;
132 
133 	sc = clknode_get_softc(clk);
134 
135 	/* Calculate divisor frequency */
136 	if (sc->clk_descr->clk_type & CLK_MASK_DIV) {
137 		uint32_t msk;
138 
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;
143 		*freq /= reg;
144 	}
145 	return (0);
146 }
147 
148 #define DIV_TIMEOUT	100
149 
150 static int
jz4780_clk_gen_set_freq(struct clknode * clk,uint64_t fin,uint64_t * fout,int flags,int * stop)151 jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
152     uint64_t *fout, int flags, int *stop)
153 {
154 	struct jz4780_clk_gen_sc *sc;
155 	uint64_t _fout;
156 	uint32_t divider, div_reg, div_msk, reg, div_l, div_h;
157 	int rv;
158 
159 	sc = clknode_get_softc(clk);
160 
161 	/* Find closest divider */
162 	div_l = howmany(fin, *fout);
163 	div_h = fin / *fout;
164 	divider = abs((int64_t)*fout - (fin / div_l)) <
165 	    abs((int64_t)*fout - (fin / div_h)) ? div_l : div_h;
166 
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;
170 	if (divider == 0)
171 		divider = 1;
172 
173 	_fout = fin / divider;
174 
175 	/* Rounding */
176 	if ((flags & CLK_SET_ROUND_UP) && (*fout > _fout))
177 		div_reg--;
178 	else if ((flags & CLK_SET_ROUND_DOWN) && (*fout < _fout))
179 		div_reg++;
180 	if (div_reg == 0)
181 		div_reg = 1;
182 
183 	div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
184 
185 	*stop = 1;
186 	if (div_reg > div_msk + 1) {
187 		*stop = 0;
188 		div_reg = div_msk;
189 	}
190 
191 	divider = (div_reg << sc->clk_descr->clk_div.div_lg);
192 	div_reg--;
193 
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)
197 			return (ERANGE);
198 		*fout = fin / divider;
199 		return (0);
200 	}
201 
202 	CLK_LOCK(sc);
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);
215 
216 	/* Wait for busy bit to clear indicating the change is complete */
217 	rv = 0;
218 	if (sc->clk_descr->clk_div.div_busy_bit >= 0) {
219 		int i;
220 
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)))
224 				break;
225 			DELAY(1000);
226 		}
227 		if (i == DIV_TIMEOUT)
228 			rv = ETIMEDOUT;
229 	}
230 	CLK_UNLOCK(sc);
231 
232 	*fout = fin / divider;
233 	return (rv);
234 }
235 
236 static int
jz4780_clk_gen_set_mux(struct clknode * clk,int src)237 jz4780_clk_gen_set_mux(struct clknode *clk, int src)
238 {
239 	struct jz4780_clk_gen_sc *sc;
240 	uint32_t reg, msk;
241 
242 	sc = clknode_get_softc(clk);
243 
244 	/* Only mux nodes are capable of being reparented */
245 	if (!(sc->clk_descr->clk_type & CLK_MASK_MUX))
246 		return (src ? EINVAL : 0);
247 
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);
250 
251 	CLK_LOCK(sc);
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);
256 	CLK_UNLOCK(sc);
257 
258 	return (0);
259 }
260 
261 static int
jz4780_clk_gen_set_gate(struct clknode * clk,bool enable)262 jz4780_clk_gen_set_gate(struct clknode *clk, bool enable)
263 {
264 	struct jz4780_clk_gen_sc *sc;
265 	uint32_t off, reg, bit;
266 
267 	sc = clknode_get_softc(clk);
268 
269 	/* Check is clock can be gated */
270 	if (sc->clk_descr->clk_gate_bit < 0)
271 		return 0;
272 
273 	bit = sc->clk_descr->clk_gate_bit;
274 	if (bit < 32) {
275 		off = JZ_CLKGR0;
276 	} else {
277 		off = JZ_CLKGR1;
278 		bit -= 32;
279 	}
280 
281 	CLK_LOCK(sc);
282 	reg = CLK_RD_4(sc, off);
283 	if (enable)
284 		reg &= ~(1u << bit);
285 	else
286 		reg |= (1u << bit);
287 	CLK_WR_4(sc, off, reg);
288 	CLK_UNLOCK(sc);
289 
290 	return (0);
291 }
292 
jz4780_clk_gen_register(struct clkdom * clkdom,const struct jz4780_clk_descr * descr,struct mtx * dev_mtx,struct resource * mem_res)293 int jz4780_clk_gen_register(struct clkdom *clkdom,
294     const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
295     struct resource *mem_res)
296 {
297 	struct clknode_init_def clkdef;
298 	struct clknode *clk;
299 	struct jz4780_clk_gen_sc *sc;
300 
301 	clkdef.id = descr->clk_id;
302 	clkdef.name = __DECONST(char *, descr->clk_name);
303 	/* Silly const games to work around API deficiency */
304 	clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0];
305 	clkdef.flags = CLK_NODE_STATIC_STRINGS;
306 	if (descr->clk_type & CLK_MASK_MUX)
307 		clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map);
308 	else
309 		clkdef.parent_cnt = 1;
310 
311 	clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef);
312 	if (clk == NULL)
313 		return (1);
314 
315 	sc = clknode_get_softc(clk);
316 	sc->clk_mtx = dev_mtx;
317 	sc->clk_res = mem_res;
318 	sc->clk_descr = descr;
319 	clknode_register(clkdom, clk);
320 
321 	return (0);
322 }
323