xref: /freebsd/sys/dev/clk/clk_div.c (revision be82b3a0bf72ed3b5f01ac9fcd8dcd3802e3c742)
1*be82b3a0SEmmanuel Vadot /*-
2*be82b3a0SEmmanuel Vadot  * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3*be82b3a0SEmmanuel Vadot  * All rights reserved.
4*be82b3a0SEmmanuel Vadot  *
5*be82b3a0SEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
6*be82b3a0SEmmanuel Vadot  * modification, are permitted provided that the following conditions
7*be82b3a0SEmmanuel Vadot  * are met:
8*be82b3a0SEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
9*be82b3a0SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
10*be82b3a0SEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
11*be82b3a0SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
12*be82b3a0SEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
13*be82b3a0SEmmanuel Vadot  *
14*be82b3a0SEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*be82b3a0SEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*be82b3a0SEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*be82b3a0SEmmanuel Vadot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*be82b3a0SEmmanuel Vadot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*be82b3a0SEmmanuel Vadot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*be82b3a0SEmmanuel Vadot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*be82b3a0SEmmanuel Vadot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*be82b3a0SEmmanuel Vadot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*be82b3a0SEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*be82b3a0SEmmanuel Vadot  * SUCH DAMAGE.
25*be82b3a0SEmmanuel Vadot  */
26*be82b3a0SEmmanuel Vadot 
27*be82b3a0SEmmanuel Vadot #include <sys/param.h>
28*be82b3a0SEmmanuel Vadot #include <sys/conf.h>
29*be82b3a0SEmmanuel Vadot #include <sys/bus.h>
30*be82b3a0SEmmanuel Vadot #include <sys/kernel.h>
31*be82b3a0SEmmanuel Vadot #include <sys/systm.h>
32*be82b3a0SEmmanuel Vadot 
33*be82b3a0SEmmanuel Vadot #include <machine/bus.h>
34*be82b3a0SEmmanuel Vadot 
35*be82b3a0SEmmanuel Vadot #include <dev/clk/clk_div.h>
36*be82b3a0SEmmanuel Vadot 
37*be82b3a0SEmmanuel Vadot #include "clkdev_if.h"
38*be82b3a0SEmmanuel Vadot 
39*be82b3a0SEmmanuel Vadot #define	WR4(_clk, off, val)						\
40*be82b3a0SEmmanuel Vadot 	CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
41*be82b3a0SEmmanuel Vadot #define	RD4(_clk, off, val)						\
42*be82b3a0SEmmanuel Vadot 	CLKDEV_READ_4(clknode_get_device(_clk), off, val)
43*be82b3a0SEmmanuel Vadot #define	MD4(_clk, off, clr, set )					\
44*be82b3a0SEmmanuel Vadot 	CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
45*be82b3a0SEmmanuel Vadot #define	DEVICE_LOCK(_clk)							\
46*be82b3a0SEmmanuel Vadot 	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
47*be82b3a0SEmmanuel Vadot #define	DEVICE_UNLOCK(_clk)						\
48*be82b3a0SEmmanuel Vadot 	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
49*be82b3a0SEmmanuel Vadot 
50*be82b3a0SEmmanuel Vadot static int clknode_div_init(struct clknode *clk, device_t dev);
51*be82b3a0SEmmanuel Vadot static int clknode_div_recalc(struct clknode *clk, uint64_t *req);
52*be82b3a0SEmmanuel Vadot static int clknode_div_set_freq(struct clknode *clknode, uint64_t fin,
53*be82b3a0SEmmanuel Vadot     uint64_t *fout, int flag, int *stop);
54*be82b3a0SEmmanuel Vadot 
55*be82b3a0SEmmanuel Vadot struct clknode_div_sc {
56*be82b3a0SEmmanuel Vadot 	struct mtx	*mtx;
57*be82b3a0SEmmanuel Vadot 	struct resource *mem_res;
58*be82b3a0SEmmanuel Vadot 	uint32_t	offset;
59*be82b3a0SEmmanuel Vadot 	uint32_t	i_shift;
60*be82b3a0SEmmanuel Vadot 	uint32_t	i_mask;
61*be82b3a0SEmmanuel Vadot 	uint32_t	i_width;
62*be82b3a0SEmmanuel Vadot 	uint32_t	f_shift;
63*be82b3a0SEmmanuel Vadot 	uint32_t	f_mask;
64*be82b3a0SEmmanuel Vadot 	uint32_t	f_width;
65*be82b3a0SEmmanuel Vadot 	int		div_flags;
66*be82b3a0SEmmanuel Vadot 	uint32_t	divider;	/* in natural form */
67*be82b3a0SEmmanuel Vadot 
68*be82b3a0SEmmanuel Vadot 	struct clk_div_table	*div_table;
69*be82b3a0SEmmanuel Vadot };
70*be82b3a0SEmmanuel Vadot 
71*be82b3a0SEmmanuel Vadot static clknode_method_t clknode_div_methods[] = {
72*be82b3a0SEmmanuel Vadot 	/* Device interface */
73*be82b3a0SEmmanuel Vadot 	CLKNODEMETHOD(clknode_init,		clknode_div_init),
74*be82b3a0SEmmanuel Vadot 	CLKNODEMETHOD(clknode_recalc_freq,	clknode_div_recalc),
75*be82b3a0SEmmanuel Vadot 	CLKNODEMETHOD(clknode_set_freq,		clknode_div_set_freq),
76*be82b3a0SEmmanuel Vadot 	CLKNODEMETHOD_END
77*be82b3a0SEmmanuel Vadot };
78*be82b3a0SEmmanuel Vadot DEFINE_CLASS_1(clknode_div, clknode_div_class, clknode_div_methods,
79*be82b3a0SEmmanuel Vadot    sizeof(struct clknode_div_sc), clknode_class);
80*be82b3a0SEmmanuel Vadot 
81*be82b3a0SEmmanuel Vadot static uint32_t
clknode_div_table_get_divider(struct clknode_div_sc * sc,uint32_t divider)82*be82b3a0SEmmanuel Vadot clknode_div_table_get_divider(struct clknode_div_sc *sc, uint32_t divider)
83*be82b3a0SEmmanuel Vadot {
84*be82b3a0SEmmanuel Vadot 	struct clk_div_table *table;
85*be82b3a0SEmmanuel Vadot 
86*be82b3a0SEmmanuel Vadot 	if (!(sc->div_flags & CLK_DIV_WITH_TABLE))
87*be82b3a0SEmmanuel Vadot 		return (divider);
88*be82b3a0SEmmanuel Vadot 
89*be82b3a0SEmmanuel Vadot 	for (table = sc->div_table; table->divider != 0; table++)
90*be82b3a0SEmmanuel Vadot 		if (table->value == sc->divider)
91*be82b3a0SEmmanuel Vadot 			return (table->divider);
92*be82b3a0SEmmanuel Vadot 
93*be82b3a0SEmmanuel Vadot 	return (0);
94*be82b3a0SEmmanuel Vadot }
95*be82b3a0SEmmanuel Vadot 
96*be82b3a0SEmmanuel Vadot static int
clknode_div_table_get_value(struct clknode_div_sc * sc,uint32_t * divider)97*be82b3a0SEmmanuel Vadot clknode_div_table_get_value(struct clknode_div_sc *sc, uint32_t *divider)
98*be82b3a0SEmmanuel Vadot {
99*be82b3a0SEmmanuel Vadot 	struct clk_div_table *table;
100*be82b3a0SEmmanuel Vadot 
101*be82b3a0SEmmanuel Vadot 	if (!(sc->div_flags & CLK_DIV_WITH_TABLE))
102*be82b3a0SEmmanuel Vadot 		return (0);
103*be82b3a0SEmmanuel Vadot 
104*be82b3a0SEmmanuel Vadot 	for (table = sc->div_table; table->divider != 0; table++)
105*be82b3a0SEmmanuel Vadot 		if (table->divider == *divider) {
106*be82b3a0SEmmanuel Vadot 			*divider = table->value;
107*be82b3a0SEmmanuel Vadot 			return (0);
108*be82b3a0SEmmanuel Vadot 		}
109*be82b3a0SEmmanuel Vadot 
110*be82b3a0SEmmanuel Vadot 	return (ENOENT);
111*be82b3a0SEmmanuel Vadot }
112*be82b3a0SEmmanuel Vadot 
113*be82b3a0SEmmanuel Vadot static int
clknode_div_init(struct clknode * clk,device_t dev)114*be82b3a0SEmmanuel Vadot clknode_div_init(struct clknode *clk, device_t dev)
115*be82b3a0SEmmanuel Vadot {
116*be82b3a0SEmmanuel Vadot 	uint32_t reg;
117*be82b3a0SEmmanuel Vadot 	struct clknode_div_sc *sc;
118*be82b3a0SEmmanuel Vadot 	uint32_t i_div, f_div;
119*be82b3a0SEmmanuel Vadot 	int rv;
120*be82b3a0SEmmanuel Vadot 
121*be82b3a0SEmmanuel Vadot 	sc = clknode_get_softc(clk);
122*be82b3a0SEmmanuel Vadot 
123*be82b3a0SEmmanuel Vadot 	DEVICE_LOCK(clk);
124*be82b3a0SEmmanuel Vadot 	rv = RD4(clk, sc->offset, &reg);
125*be82b3a0SEmmanuel Vadot 	DEVICE_UNLOCK(clk);
126*be82b3a0SEmmanuel Vadot 	if (rv != 0)
127*be82b3a0SEmmanuel Vadot 		return (rv);
128*be82b3a0SEmmanuel Vadot 
129*be82b3a0SEmmanuel Vadot 	i_div = (reg >> sc->i_shift) & sc->i_mask;
130*be82b3a0SEmmanuel Vadot 	if (!(sc->div_flags & CLK_DIV_WITH_TABLE) &&
131*be82b3a0SEmmanuel Vadot 	    !(sc->div_flags & CLK_DIV_ZERO_BASED))
132*be82b3a0SEmmanuel Vadot 		i_div++;
133*be82b3a0SEmmanuel Vadot 	f_div = (reg >> sc->f_shift) & sc->f_mask;
134*be82b3a0SEmmanuel Vadot 	sc->divider = i_div << sc->f_width | f_div;
135*be82b3a0SEmmanuel Vadot 
136*be82b3a0SEmmanuel Vadot 	sc->divider = clknode_div_table_get_divider(sc, sc->divider);
137*be82b3a0SEmmanuel Vadot 	if (sc->divider == 0)
138*be82b3a0SEmmanuel Vadot 		panic("%s: divider is zero!\n", clknode_get_name(clk));
139*be82b3a0SEmmanuel Vadot 
140*be82b3a0SEmmanuel Vadot 	clknode_init_parent_idx(clk, 0);
141*be82b3a0SEmmanuel Vadot 	return(0);
142*be82b3a0SEmmanuel Vadot }
143*be82b3a0SEmmanuel Vadot 
144*be82b3a0SEmmanuel Vadot static int
clknode_div_recalc(struct clknode * clk,uint64_t * freq)145*be82b3a0SEmmanuel Vadot clknode_div_recalc(struct clknode *clk, uint64_t *freq)
146*be82b3a0SEmmanuel Vadot {
147*be82b3a0SEmmanuel Vadot 	struct clknode_div_sc *sc;
148*be82b3a0SEmmanuel Vadot 
149*be82b3a0SEmmanuel Vadot 	sc = clknode_get_softc(clk);
150*be82b3a0SEmmanuel Vadot 	if (sc->divider == 0) {
151*be82b3a0SEmmanuel Vadot 		printf("%s: %s divider is zero!\n", clknode_get_name(clk),
152*be82b3a0SEmmanuel Vadot 		__func__);
153*be82b3a0SEmmanuel Vadot 		*freq = 0;
154*be82b3a0SEmmanuel Vadot 		return(EINVAL);
155*be82b3a0SEmmanuel Vadot 	}
156*be82b3a0SEmmanuel Vadot 	*freq = (*freq << sc->f_width) / sc->divider;
157*be82b3a0SEmmanuel Vadot 	return (0);
158*be82b3a0SEmmanuel Vadot }
159*be82b3a0SEmmanuel Vadot 
160*be82b3a0SEmmanuel Vadot static int
clknode_div_set_freq(struct clknode * clk,uint64_t fin,uint64_t * fout,int flags,int * stop)161*be82b3a0SEmmanuel Vadot clknode_div_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
162*be82b3a0SEmmanuel Vadot   int flags, int *stop)
163*be82b3a0SEmmanuel Vadot {
164*be82b3a0SEmmanuel Vadot 	struct clknode_div_sc *sc;
165*be82b3a0SEmmanuel Vadot 	uint64_t divider, _fin, _fout;
166*be82b3a0SEmmanuel Vadot 	uint32_t reg, i_div, f_div, hw_i_div;
167*be82b3a0SEmmanuel Vadot 	int rv;
168*be82b3a0SEmmanuel Vadot 
169*be82b3a0SEmmanuel Vadot 	sc = clknode_get_softc(clk);
170*be82b3a0SEmmanuel Vadot 
171*be82b3a0SEmmanuel Vadot 	/* For fractional divider. */
172*be82b3a0SEmmanuel Vadot 	_fin = fin << sc->f_width;
173*be82b3a0SEmmanuel Vadot 	divider = (_fin + *fout / 2) / *fout;
174*be82b3a0SEmmanuel Vadot 	_fout = _fin / divider;
175*be82b3a0SEmmanuel Vadot 
176*be82b3a0SEmmanuel Vadot 	/* Rounding. */
177*be82b3a0SEmmanuel Vadot 	if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout))
178*be82b3a0SEmmanuel Vadot 		divider--;
179*be82b3a0SEmmanuel Vadot 	else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout))
180*be82b3a0SEmmanuel Vadot 		divider++;
181*be82b3a0SEmmanuel Vadot 
182*be82b3a0SEmmanuel Vadot 	/* Break divider into integer and fractional parts. */
183*be82b3a0SEmmanuel Vadot 	i_div = divider >> sc->f_width;
184*be82b3a0SEmmanuel Vadot 	f_div = divider  & sc->f_mask;
185*be82b3a0SEmmanuel Vadot 
186*be82b3a0SEmmanuel Vadot 	if (i_div == 0) {
187*be82b3a0SEmmanuel Vadot 		printf("%s: %s integer divider is zero!\n",
188*be82b3a0SEmmanuel Vadot 		     clknode_get_name(clk), __func__);
189*be82b3a0SEmmanuel Vadot 		return(EINVAL);
190*be82b3a0SEmmanuel Vadot 	}
191*be82b3a0SEmmanuel Vadot 
192*be82b3a0SEmmanuel Vadot 	*stop = 1;
193*be82b3a0SEmmanuel Vadot 	hw_i_div = i_div;
194*be82b3a0SEmmanuel Vadot 	if (sc->div_flags & CLK_DIV_WITH_TABLE) {
195*be82b3a0SEmmanuel Vadot 		if (clknode_div_table_get_value(sc, &hw_i_div) != 0)
196*be82b3a0SEmmanuel Vadot 				return (ERANGE);
197*be82b3a0SEmmanuel Vadot 	} else {
198*be82b3a0SEmmanuel Vadot 		if (!(sc->div_flags & CLK_DIV_ZERO_BASED))
199*be82b3a0SEmmanuel Vadot 			hw_i_div--;
200*be82b3a0SEmmanuel Vadot 
201*be82b3a0SEmmanuel Vadot 		if (i_div > sc->i_mask) {
202*be82b3a0SEmmanuel Vadot 			/* XXX Pass to parent or return error? */
203*be82b3a0SEmmanuel Vadot 			printf("%s: %s integer divider is too big: %u\n",
204*be82b3a0SEmmanuel Vadot 			    clknode_get_name(clk), __func__, i_div);
205*be82b3a0SEmmanuel Vadot 			hw_i_div = sc->i_mask;
206*be82b3a0SEmmanuel Vadot 			*stop = 0;
207*be82b3a0SEmmanuel Vadot 		}
208*be82b3a0SEmmanuel Vadot 		i_div = hw_i_div;
209*be82b3a0SEmmanuel Vadot 		if (!(sc->div_flags & CLK_DIV_ZERO_BASED))
210*be82b3a0SEmmanuel Vadot 			i_div++;
211*be82b3a0SEmmanuel Vadot 	}
212*be82b3a0SEmmanuel Vadot 
213*be82b3a0SEmmanuel Vadot 	divider = i_div << sc->f_width | f_div;
214*be82b3a0SEmmanuel Vadot 
215*be82b3a0SEmmanuel Vadot 	if ((flags & CLK_SET_DRYRUN) == 0) {
216*be82b3a0SEmmanuel Vadot 		if ((*stop != 0) &&
217*be82b3a0SEmmanuel Vadot 		    ((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
218*be82b3a0SEmmanuel Vadot 		    (*fout != (_fin / divider)))
219*be82b3a0SEmmanuel Vadot 			return (ERANGE);
220*be82b3a0SEmmanuel Vadot 
221*be82b3a0SEmmanuel Vadot 		DEVICE_LOCK(clk);
222*be82b3a0SEmmanuel Vadot 		rv = MD4(clk, sc->offset,
223*be82b3a0SEmmanuel Vadot 		    (sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift),
224*be82b3a0SEmmanuel Vadot 		    (hw_i_div << sc->i_shift) | (f_div << sc->f_shift));
225*be82b3a0SEmmanuel Vadot 		if (rv != 0) {
226*be82b3a0SEmmanuel Vadot 			DEVICE_UNLOCK(clk);
227*be82b3a0SEmmanuel Vadot 			return (rv);
228*be82b3a0SEmmanuel Vadot 		}
229*be82b3a0SEmmanuel Vadot 		RD4(clk, sc->offset, &reg);
230*be82b3a0SEmmanuel Vadot 		DEVICE_UNLOCK(clk);
231*be82b3a0SEmmanuel Vadot 
232*be82b3a0SEmmanuel Vadot 		sc->divider = divider;
233*be82b3a0SEmmanuel Vadot 	}
234*be82b3a0SEmmanuel Vadot 
235*be82b3a0SEmmanuel Vadot 	*fout = _fin / divider;
236*be82b3a0SEmmanuel Vadot 	return (0);
237*be82b3a0SEmmanuel Vadot }
238*be82b3a0SEmmanuel Vadot 
239*be82b3a0SEmmanuel Vadot int
clknode_div_register(struct clkdom * clkdom,struct clk_div_def * clkdef)240*be82b3a0SEmmanuel Vadot clknode_div_register(struct clkdom *clkdom, struct clk_div_def *clkdef)
241*be82b3a0SEmmanuel Vadot {
242*be82b3a0SEmmanuel Vadot 	struct clknode *clk;
243*be82b3a0SEmmanuel Vadot 	struct clknode_div_sc *sc;
244*be82b3a0SEmmanuel Vadot 
245*be82b3a0SEmmanuel Vadot 	clk = clknode_create(clkdom, &clknode_div_class, &clkdef->clkdef);
246*be82b3a0SEmmanuel Vadot 	if (clk == NULL)
247*be82b3a0SEmmanuel Vadot 		return (1);
248*be82b3a0SEmmanuel Vadot 
249*be82b3a0SEmmanuel Vadot 	sc = clknode_get_softc(clk);
250*be82b3a0SEmmanuel Vadot 	sc->offset = clkdef->offset;
251*be82b3a0SEmmanuel Vadot 	sc->i_shift = clkdef->i_shift;
252*be82b3a0SEmmanuel Vadot 	sc->i_width = clkdef->i_width;
253*be82b3a0SEmmanuel Vadot 	sc->i_mask = (1 << clkdef->i_width) - 1;
254*be82b3a0SEmmanuel Vadot 	sc->f_shift = clkdef->f_shift;
255*be82b3a0SEmmanuel Vadot 	sc->f_width = clkdef->f_width;
256*be82b3a0SEmmanuel Vadot 	sc->f_mask = (1 << clkdef->f_width) - 1;
257*be82b3a0SEmmanuel Vadot 	sc->div_flags = clkdef->div_flags;
258*be82b3a0SEmmanuel Vadot 	sc->div_table = clkdef->div_table;
259*be82b3a0SEmmanuel Vadot 
260*be82b3a0SEmmanuel Vadot 	clknode_register(clkdom, clk);
261*be82b3a0SEmmanuel Vadot 	return (0);
262*be82b3a0SEmmanuel Vadot }
263