]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/ingenic/jz4780_clk_gen.c
Update compiler-rt to release_39 branch r288513. Since this contains a
[FreeBSD/FreeBSD.git] / sys / mips / ingenic / jz4780_clk_gen.c
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 */
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
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
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
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
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
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;
157         int rv;
158
159         sc = clknode_get_softc(clk);
160
161         divider = fin / *fout;
162
163         /* Adjust for divider multiplier */
164         div_reg = divider >> sc->clk_descr->clk_div.div_lg;
165         divider = div_reg << sc->clk_descr->clk_div.div_lg;
166
167         _fout = fin / divider;
168
169         /* Rounding */
170         if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout))
171                 div_reg--;
172         else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout))
173                 div_reg++;
174         if (div_reg == 0)
175                 div_reg = 1;
176
177         div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
178
179         *stop = 1;
180         if (div_reg > div_msk + 1) {
181                 *stop = 0;
182                 div_reg = div_msk;
183         }
184
185         divider = (div_reg << sc->clk_descr->clk_div.div_lg);
186         div_reg--;
187
188         if ((flags & CLK_SET_DRYRUN) != 0) {
189                 if (*stop != 0 && *fout != fin / divider &&
190                     (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0)
191                         return (ERANGE);
192                 *fout = fin / divider;
193                 return (0);
194         }
195
196         CLK_LOCK(sc);
197         /* Apply the new divider value */
198         reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
199         reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift);
200         reg |= (div_reg << sc->clk_descr->clk_div.div_shift);
201         /* Set the change enable bit, it present */
202         if (sc->clk_descr->clk_div.div_ce_bit >= 0)
203                 reg |= (1u << sc->clk_descr->clk_div.div_ce_bit);
204         /* Clear stop bit, it present */
205         if (sc->clk_descr->clk_div.div_st_bit >= 0)
206                 reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit);
207         /* Initiate the change */
208         CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg);
209
210         /* Wait for busy bit to clear indicating the change is complete */
211         rv = 0;
212         if (sc->clk_descr->clk_div.div_busy_bit >= 0) {
213                 int i;
214
215                 for (i = 0;  i < DIV_TIMEOUT; i++) {
216                         reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
217                         if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit)))
218                                 break;
219                         DELAY(1000);
220                 }
221                 if (i == DIV_TIMEOUT)
222                         rv = ETIMEDOUT;
223         }
224         CLK_UNLOCK(sc);
225
226         *fout = fin / divider;
227         return (rv);
228 }
229
230 static int
231 jz4780_clk_gen_set_mux(struct clknode *clk, int src)
232 {
233         struct jz4780_clk_gen_sc *sc;
234         uint32_t reg, msk;
235
236         sc = clknode_get_softc(clk);
237
238         /* Only mux nodes are capable of being reparented */
239         if (!(sc->clk_descr->clk_type & CLK_MASK_MUX))
240                 return (src ? EINVAL : 0);
241
242         msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
243         src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map);
244
245         CLK_LOCK(sc);
246         reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
247         reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift);
248         reg |=  (src << sc->clk_descr->clk_mux.mux_shift);
249         CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg);
250         CLK_UNLOCK(sc);
251
252         return (0);
253 }
254
255 static int
256 jz4780_clk_gen_set_gate(struct clknode *clk, bool enable)
257 {
258         struct jz4780_clk_gen_sc *sc;
259         uint32_t off, reg, bit;
260
261         sc = clknode_get_softc(clk);
262
263         /* Check is clock can be gated */
264         if (sc->clk_descr->clk_gate_bit < 0)
265                 return 0;
266
267         bit = sc->clk_descr->clk_gate_bit;
268         if (bit < 32) {
269                 off = JZ_CLKGR0;
270         } else {
271                 off = JZ_CLKGR1;
272                 bit -= 32;
273         }
274
275         CLK_LOCK(sc);
276         reg = CLK_RD_4(sc, off);
277         if (enable)
278                 reg &= ~(1u << bit);
279         else
280                 reg |= (1u << bit);
281         CLK_WR_4(sc, off, reg);
282         CLK_UNLOCK(sc);
283
284         return (0);
285 }
286
287
288 int jz4780_clk_gen_register(struct clkdom *clkdom,
289     const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
290     struct resource *mem_res)
291 {
292         struct clknode_init_def clkdef;
293         struct clknode *clk;
294         struct jz4780_clk_gen_sc *sc;
295
296         clkdef.id = descr->clk_id;
297         clkdef.name = __DECONST(char *, descr->clk_name);
298         /* Silly const games to work around API deficiency */
299         clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0];
300         clkdef.flags = CLK_NODE_STATIC_STRINGS;
301         if (descr->clk_type & CLK_MASK_MUX)
302                 clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map);
303         else
304                 clkdef.parent_cnt = 1;
305
306         clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef);
307         if (clk == NULL)
308                 return (1);
309
310         sc = clknode_get_softc(clk);
311         sc->clk_mtx = dev_mtx;
312         sc->clk_res = mem_res;
313         sc->clk_descr = descr;
314         clknode_register(clkdom, clk);
315
316         return (0);
317 }