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, ®);
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, ®);
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