1/*-
2 * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
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 */
49static int jz4780_clk_gen_init(struct clknode *clk, device_t dev);
50static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq);
51static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
52    uint64_t *fout, int flags, int *stop);
53static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable);
54static int jz4780_clk_gen_set_mux(struct clknode *clk, int src);
55
56struct 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 */
66static 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};
75DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods,
76       sizeof(struct jz4780_clk_gen_sc), clknode_class);
77
78static inline unsigned
79mux_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
93static inline unsigned
94reg_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
105static int
106jz4780_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
127static int
128jz4780_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
150static int
151jz4780_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
236static int
237jz4780_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
261static int
262jz4780_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
293int 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