xref: /freebsd/sys/dev/clk/allwinner/aw_clk_mipi.c (revision be82b3a0bf72ed3b5f01ac9fcd8dcd3802e3c742)
1e37e8677SEmmanuel Vadot /*-
2e37e8677SEmmanuel Vadot  * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
3e37e8677SEmmanuel Vadot  *
4e37e8677SEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
5e37e8677SEmmanuel Vadot  * modification, are permitted provided that the following conditions
6e37e8677SEmmanuel Vadot  * are met:
7e37e8677SEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
8e37e8677SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
9e37e8677SEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
10e37e8677SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
11e37e8677SEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
12e37e8677SEmmanuel Vadot  *
13e37e8677SEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14e37e8677SEmmanuel Vadot  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15e37e8677SEmmanuel Vadot  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16e37e8677SEmmanuel Vadot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17e37e8677SEmmanuel Vadot  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18e37e8677SEmmanuel Vadot  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19e37e8677SEmmanuel Vadot  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20e37e8677SEmmanuel Vadot  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21e37e8677SEmmanuel Vadot  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22e37e8677SEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23e37e8677SEmmanuel Vadot  * SUCH DAMAGE.
24e37e8677SEmmanuel Vadot  */
25e37e8677SEmmanuel Vadot 
26e37e8677SEmmanuel Vadot #include <sys/param.h>
27e37e8677SEmmanuel Vadot #include <sys/systm.h>
28e37e8677SEmmanuel Vadot #include <sys/bus.h>
29e37e8677SEmmanuel Vadot 
30*be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
31e37e8677SEmmanuel Vadot 
32e37e8677SEmmanuel Vadot #include <dev/clk/allwinner/aw_clk.h>
33e37e8677SEmmanuel Vadot #include <dev/clk/allwinner/aw_clk_mipi.h>
34e37e8677SEmmanuel Vadot 
35e37e8677SEmmanuel Vadot #include "clkdev_if.h"
36e37e8677SEmmanuel Vadot 
37e37e8677SEmmanuel Vadot /* #define	dprintf(format, arg...)	printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
38e37e8677SEmmanuel Vadot #define	dprintf(format, arg...)
39e37e8677SEmmanuel Vadot 
40e37e8677SEmmanuel Vadot /*
41e37e8677SEmmanuel Vadot  * clknode for PLL_MIPI :
42e37e8677SEmmanuel Vadot  *
43e37e8677SEmmanuel Vadot  * clk = (pll_video0 * n * k) / m when vfb_sel=0
44e37e8677SEmmanuel Vadot  * clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=1
45e37e8677SEmmanuel Vadot  *
46e37e8677SEmmanuel Vadot  */
47e37e8677SEmmanuel Vadot 
48e37e8677SEmmanuel Vadot struct aw_clk_mipi_sc {
49e37e8677SEmmanuel Vadot 	uint32_t	offset;
50e37e8677SEmmanuel Vadot 
51e37e8677SEmmanuel Vadot 	struct aw_clk_factor	k;
52e37e8677SEmmanuel Vadot 	struct aw_clk_factor	m;
53e37e8677SEmmanuel Vadot 	struct aw_clk_factor	n;
54e37e8677SEmmanuel Vadot 
55e37e8677SEmmanuel Vadot 	uint64_t		min_freq;
56e37e8677SEmmanuel Vadot 	uint64_t		max_freq;
57e37e8677SEmmanuel Vadot 
58e37e8677SEmmanuel Vadot 	uint32_t	gate_shift;
59e37e8677SEmmanuel Vadot 	uint32_t	lock_shift;
60e37e8677SEmmanuel Vadot 	uint32_t	lock_retries;
61e37e8677SEmmanuel Vadot 
62e37e8677SEmmanuel Vadot 	uint32_t	flags;
63e37e8677SEmmanuel Vadot };
64e37e8677SEmmanuel Vadot 
65e37e8677SEmmanuel Vadot #define	WRITE4(_clk, off, val)						\
66e37e8677SEmmanuel Vadot 	CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
67e37e8677SEmmanuel Vadot #define	READ4(_clk, off, val)						\
68e37e8677SEmmanuel Vadot 	CLKDEV_READ_4(clknode_get_device(_clk), off, val)
69e37e8677SEmmanuel Vadot #define	DEVICE_LOCK(_clk)							\
70e37e8677SEmmanuel Vadot 	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
71e37e8677SEmmanuel Vadot #define	DEVICE_UNLOCK(_clk)						\
72e37e8677SEmmanuel Vadot 	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
73e37e8677SEmmanuel Vadot 
74e37e8677SEmmanuel Vadot #define	LDO1_EN_SHIFT	23
75e37e8677SEmmanuel Vadot #define	LDO2_EN_SHIFT	22
76e37e8677SEmmanuel Vadot #define	VFB_SEL_SHIFT	16
77e37e8677SEmmanuel Vadot 
78e37e8677SEmmanuel Vadot static int
aw_clk_mipi_init(struct clknode * clk,device_t dev)79e37e8677SEmmanuel Vadot aw_clk_mipi_init(struct clknode *clk, device_t dev)
80e37e8677SEmmanuel Vadot {
81e37e8677SEmmanuel Vadot 
82e37e8677SEmmanuel Vadot 	clknode_init_parent_idx(clk, 0);
83e37e8677SEmmanuel Vadot 	return (0);
84e37e8677SEmmanuel Vadot }
85e37e8677SEmmanuel Vadot 
86e37e8677SEmmanuel Vadot static int
aw_clk_mipi_set_gate(struct clknode * clk,bool enable)87e37e8677SEmmanuel Vadot aw_clk_mipi_set_gate(struct clknode *clk, bool enable)
88e37e8677SEmmanuel Vadot {
89e37e8677SEmmanuel Vadot 	struct aw_clk_mipi_sc *sc;
90e37e8677SEmmanuel Vadot 	uint32_t val;
91e37e8677SEmmanuel Vadot 
92e37e8677SEmmanuel Vadot 	sc = clknode_get_softc(clk);
93e37e8677SEmmanuel Vadot 
94e37e8677SEmmanuel Vadot 	dprintf("%sabling gate\n", enable ? "En" : "Dis");
95e37e8677SEmmanuel Vadot 	DEVICE_LOCK(clk);
96e37e8677SEmmanuel Vadot 	READ4(clk, sc->offset, &val);
97e37e8677SEmmanuel Vadot 	if (enable) {
98e37e8677SEmmanuel Vadot 		val |= (1 << sc->gate_shift);
99e37e8677SEmmanuel Vadot 		val |= (1 << LDO1_EN_SHIFT);
100e37e8677SEmmanuel Vadot 		val |= (1 << LDO2_EN_SHIFT);
101e37e8677SEmmanuel Vadot 	} else {
102e37e8677SEmmanuel Vadot 		val &= ~(1 << sc->gate_shift);
103e37e8677SEmmanuel Vadot 		val &= ~(1 << LDO1_EN_SHIFT);
104e37e8677SEmmanuel Vadot 		val &= ~(1 << LDO2_EN_SHIFT);
105e37e8677SEmmanuel Vadot 	}
106e37e8677SEmmanuel Vadot 	WRITE4(clk, sc->offset, val);
107e37e8677SEmmanuel Vadot 	DEVICE_UNLOCK(clk);
108e37e8677SEmmanuel Vadot 
109e37e8677SEmmanuel Vadot 	return (0);
110e37e8677SEmmanuel Vadot }
111e37e8677SEmmanuel Vadot 
112e37e8677SEmmanuel Vadot static uint64_t
aw_clk_mipi_find_best(struct aw_clk_mipi_sc * sc,uint64_t fparent,uint64_t * fout,uint32_t * factor_k,uint32_t * factor_m,uint32_t * factor_n)113e37e8677SEmmanuel Vadot aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout,
114e37e8677SEmmanuel Vadot     uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n)
115e37e8677SEmmanuel Vadot {
116e37e8677SEmmanuel Vadot 	uint64_t cur, best;
117e37e8677SEmmanuel Vadot 	uint32_t n, k, m;
118e37e8677SEmmanuel Vadot 
119e37e8677SEmmanuel Vadot 	best = 0;
120e37e8677SEmmanuel Vadot 	*factor_n = 0;
121e37e8677SEmmanuel Vadot 	*factor_k = 0;
122e37e8677SEmmanuel Vadot 	*factor_m = 0;
123e37e8677SEmmanuel Vadot 
124e37e8677SEmmanuel Vadot 	for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); n++) {
125e37e8677SEmmanuel Vadot 		for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); k++) {
126e37e8677SEmmanuel Vadot 			for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); m++) {
127e37e8677SEmmanuel Vadot 				cur = (fparent * n * k) / m;
128e37e8677SEmmanuel Vadot 				if ((*fout - cur) < (*fout - best)) {
129e37e8677SEmmanuel Vadot 					best = cur;
130e37e8677SEmmanuel Vadot 					*factor_n = n;
131e37e8677SEmmanuel Vadot 					*factor_k = k;
132e37e8677SEmmanuel Vadot 					*factor_m = m;
133e37e8677SEmmanuel Vadot 				}
134e37e8677SEmmanuel Vadot 				if (best == *fout)
135e37e8677SEmmanuel Vadot 					return (best);
136e37e8677SEmmanuel Vadot 			}
137e37e8677SEmmanuel Vadot 		}
138e37e8677SEmmanuel Vadot 	}
139e37e8677SEmmanuel Vadot 
140e37e8677SEmmanuel Vadot 	return best;
141e37e8677SEmmanuel Vadot }
142e37e8677SEmmanuel Vadot 
143e37e8677SEmmanuel Vadot static int
aw_clk_mipi_set_freq(struct clknode * clk,uint64_t fparent,uint64_t * fout,int flags,int * stop)144e37e8677SEmmanuel Vadot aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
145e37e8677SEmmanuel Vadot     int flags, int *stop)
146e37e8677SEmmanuel Vadot {
147e37e8677SEmmanuel Vadot 	struct aw_clk_mipi_sc *sc;
148e37e8677SEmmanuel Vadot 	uint64_t best = 0;
149e37e8677SEmmanuel Vadot 	uint32_t best_k, best_m, best_n;
150e37e8677SEmmanuel Vadot 	uint32_t k, m, n;
151e37e8677SEmmanuel Vadot 	uint32_t val;
152e37e8677SEmmanuel Vadot 	uint32_t retry;
153e37e8677SEmmanuel Vadot 
154e37e8677SEmmanuel Vadot 	sc = clknode_get_softc(clk);
155e37e8677SEmmanuel Vadot 
156e37e8677SEmmanuel Vadot 	best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n);
157e37e8677SEmmanuel Vadot 
158e37e8677SEmmanuel Vadot 	if (best < sc->min_freq ||
159e37e8677SEmmanuel Vadot 	    best > sc->max_freq) {
160e37e8677SEmmanuel Vadot 		printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
161e37e8677SEmmanuel Vadot 		    __func__, best, clknode_get_name(clk),
162e37e8677SEmmanuel Vadot 		    sc->min_freq, sc->max_freq);
163e37e8677SEmmanuel Vadot 		return (ERANGE);
164e37e8677SEmmanuel Vadot 	}
165e37e8677SEmmanuel Vadot 	if ((flags & CLK_SET_DRYRUN) != 0) {
166e37e8677SEmmanuel Vadot 		*fout = best;
167e37e8677SEmmanuel Vadot 		*stop = 1;
168e37e8677SEmmanuel Vadot 		return (0);
169e37e8677SEmmanuel Vadot 	}
170e37e8677SEmmanuel Vadot 
171e37e8677SEmmanuel Vadot 	DEVICE_LOCK(clk);
172e37e8677SEmmanuel Vadot 	READ4(clk, sc->offset, &val);
173e37e8677SEmmanuel Vadot 	/* Disable clock during freq changes */
174e37e8677SEmmanuel Vadot 	val &= ~(1 << sc->gate_shift);
175e37e8677SEmmanuel Vadot 	WRITE4(clk, sc->offset, val);
176e37e8677SEmmanuel Vadot 
177e37e8677SEmmanuel Vadot 	k = aw_clk_factor_get_value(&sc->k, best_k);
178e37e8677SEmmanuel Vadot 	n = aw_clk_factor_get_value(&sc->n, best_n);
179e37e8677SEmmanuel Vadot 	m = aw_clk_factor_get_value(&sc->m, best_m);
180e37e8677SEmmanuel Vadot 	val &= ~sc->k.mask;
181e37e8677SEmmanuel Vadot 	val &= ~sc->m.mask;
182e37e8677SEmmanuel Vadot 	val &= ~sc->n.mask;
183e37e8677SEmmanuel Vadot 	val |= k << sc->k.shift;
184e37e8677SEmmanuel Vadot 	val |= m << sc->m.shift;
185e37e8677SEmmanuel Vadot 	val |= n << sc->n.shift;
186e37e8677SEmmanuel Vadot 
187e37e8677SEmmanuel Vadot 	/* Write the clock changes */
188e37e8677SEmmanuel Vadot 	WRITE4(clk, sc->offset, val);
189e37e8677SEmmanuel Vadot 
190e37e8677SEmmanuel Vadot 	/* Enable clock now that we've change it */
191e37e8677SEmmanuel Vadot 	val |= 1 << sc->gate_shift;
192e37e8677SEmmanuel Vadot 	WRITE4(clk, sc->offset, val);
193e37e8677SEmmanuel Vadot 	DEVICE_UNLOCK(clk);
194e37e8677SEmmanuel Vadot 
195e37e8677SEmmanuel Vadot 	for (retry = 0; retry < sc->lock_retries; retry++) {
196e37e8677SEmmanuel Vadot 		READ4(clk, sc->offset, &val);
197e37e8677SEmmanuel Vadot 		if ((val & (1 << sc->lock_shift)) != 0)
198e37e8677SEmmanuel Vadot 			break;
199e37e8677SEmmanuel Vadot 		DELAY(1000);
200e37e8677SEmmanuel Vadot 	}
201e37e8677SEmmanuel Vadot 
202e37e8677SEmmanuel Vadot 	*fout = best;
203e37e8677SEmmanuel Vadot 	*stop = 1;
204e37e8677SEmmanuel Vadot 
205e37e8677SEmmanuel Vadot 	return (0);
206e37e8677SEmmanuel Vadot }
207e37e8677SEmmanuel Vadot 
208e37e8677SEmmanuel Vadot static int
aw_clk_mipi_recalc(struct clknode * clk,uint64_t * freq)209e37e8677SEmmanuel Vadot aw_clk_mipi_recalc(struct clknode *clk, uint64_t *freq)
210e37e8677SEmmanuel Vadot {
211e37e8677SEmmanuel Vadot 	struct aw_clk_mipi_sc *sc;
212e37e8677SEmmanuel Vadot 	uint32_t val, m, n, k;
213e37e8677SEmmanuel Vadot 
214e37e8677SEmmanuel Vadot 	sc = clknode_get_softc(clk);
215e37e8677SEmmanuel Vadot 
216e37e8677SEmmanuel Vadot 	DEVICE_LOCK(clk);
217e37e8677SEmmanuel Vadot 	READ4(clk, sc->offset, &val);
218e37e8677SEmmanuel Vadot 	DEVICE_UNLOCK(clk);
219e37e8677SEmmanuel Vadot 
220e37e8677SEmmanuel Vadot 	k = aw_clk_get_factor(val, &sc->k);
221e37e8677SEmmanuel Vadot 	m = aw_clk_get_factor(val, &sc->m);
222e37e8677SEmmanuel Vadot 	n = aw_clk_get_factor(val, &sc->n);
223e37e8677SEmmanuel Vadot 
224e37e8677SEmmanuel Vadot 	*freq = (*freq * n * k) / m;
225e37e8677SEmmanuel Vadot 
226e37e8677SEmmanuel Vadot 	return (0);
227e37e8677SEmmanuel Vadot }
228e37e8677SEmmanuel Vadot 
229e37e8677SEmmanuel Vadot static clknode_method_t aw_mipi_clknode_methods[] = {
230e37e8677SEmmanuel Vadot 	/* Device interface */
231e37e8677SEmmanuel Vadot 	CLKNODEMETHOD(clknode_init,		aw_clk_mipi_init),
232e37e8677SEmmanuel Vadot 	CLKNODEMETHOD(clknode_set_gate,		aw_clk_mipi_set_gate),
233e37e8677SEmmanuel Vadot 	CLKNODEMETHOD(clknode_recalc_freq,	aw_clk_mipi_recalc),
234e37e8677SEmmanuel Vadot 	CLKNODEMETHOD(clknode_set_freq,		aw_clk_mipi_set_freq),
235e37e8677SEmmanuel Vadot 	CLKNODEMETHOD_END
236e37e8677SEmmanuel Vadot };
237e37e8677SEmmanuel Vadot 
238e37e8677SEmmanuel Vadot DEFINE_CLASS_1(aw_mipi_clknode, aw_mipi_clknode_class, aw_mipi_clknode_methods,
239e37e8677SEmmanuel Vadot     sizeof(struct aw_clk_mipi_sc), clknode_class);
240e37e8677SEmmanuel Vadot 
241e37e8677SEmmanuel Vadot int
aw_clk_mipi_register(struct clkdom * clkdom,struct aw_clk_mipi_def * clkdef)242e37e8677SEmmanuel Vadot aw_clk_mipi_register(struct clkdom *clkdom, struct aw_clk_mipi_def *clkdef)
243e37e8677SEmmanuel Vadot {
244e37e8677SEmmanuel Vadot 	struct clknode *clk;
245e37e8677SEmmanuel Vadot 	struct aw_clk_mipi_sc *sc;
246e37e8677SEmmanuel Vadot 
247e37e8677SEmmanuel Vadot 	clk = clknode_create(clkdom, &aw_mipi_clknode_class, &clkdef->clkdef);
248e37e8677SEmmanuel Vadot 	if (clk == NULL)
249e37e8677SEmmanuel Vadot 		return (1);
250e37e8677SEmmanuel Vadot 
251e37e8677SEmmanuel Vadot 	sc = clknode_get_softc(clk);
252e37e8677SEmmanuel Vadot 
253e37e8677SEmmanuel Vadot 	sc->offset = clkdef->offset;
254e37e8677SEmmanuel Vadot 
255e37e8677SEmmanuel Vadot 	sc->k.shift = clkdef->k.shift;
256e37e8677SEmmanuel Vadot 	sc->k.width = clkdef->k.width;
257e37e8677SEmmanuel Vadot 	sc->k.mask = ((1 << sc->k.width) - 1) << sc->k.shift;
258e37e8677SEmmanuel Vadot 	sc->k.value = clkdef->k.value;
259e37e8677SEmmanuel Vadot 	sc->k.flags = clkdef->k.flags;
260e37e8677SEmmanuel Vadot 	sc->k.min_value = clkdef->k.min_value;
261e37e8677SEmmanuel Vadot 
262e37e8677SEmmanuel Vadot 	sc->m.shift = clkdef->m.shift;
263e37e8677SEmmanuel Vadot 	sc->m.width = clkdef->m.width;
264e37e8677SEmmanuel Vadot 	sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
265e37e8677SEmmanuel Vadot 	sc->m.value = clkdef->m.value;
266e37e8677SEmmanuel Vadot 	sc->m.flags = clkdef->m.flags;
267e37e8677SEmmanuel Vadot 	sc->m.min_value = clkdef->m.min_value;
268e37e8677SEmmanuel Vadot 
269e37e8677SEmmanuel Vadot 	sc->n.shift = clkdef->n.shift;
270e37e8677SEmmanuel Vadot 	sc->n.width = clkdef->n.width;
271e37e8677SEmmanuel Vadot 	sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
272e37e8677SEmmanuel Vadot 	sc->n.value = clkdef->n.value;
273e37e8677SEmmanuel Vadot 	sc->n.flags = clkdef->n.flags;
274e37e8677SEmmanuel Vadot 	sc->n.min_value = clkdef->n.min_value;
275e37e8677SEmmanuel Vadot 
276e37e8677SEmmanuel Vadot 	sc->min_freq = clkdef->min_freq;
277e37e8677SEmmanuel Vadot 	sc->max_freq = clkdef->max_freq;
278e37e8677SEmmanuel Vadot 
279e37e8677SEmmanuel Vadot 	sc->gate_shift = clkdef->gate_shift;
280e37e8677SEmmanuel Vadot 
281e37e8677SEmmanuel Vadot 	sc->lock_shift = clkdef->lock_shift;
282e37e8677SEmmanuel Vadot 	sc->lock_retries = clkdef->lock_retries;
283e37e8677SEmmanuel Vadot 
284e37e8677SEmmanuel Vadot 	sc->flags = clkdef->flags;
285e37e8677SEmmanuel Vadot 
286e37e8677SEmmanuel Vadot 	clknode_register(clkdom, clk);
287e37e8677SEmmanuel Vadot 
288e37e8677SEmmanuel Vadot 	return (0);
289e37e8677SEmmanuel Vadot }
290