]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/rockchip/clk/rk_clk_pll.c
Import DTS files from Linux 4.18
[FreeBSD/FreeBSD.git] / sys / arm64 / rockchip / clk / rk_clk_pll.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37
38 #include <dev/extres/clk/clk.h>
39
40 #include <arm64/rockchip/clk/rk_clk_pll.h>
41
42 #include "clkdev_if.h"
43
44 struct rk_clk_pll_sc {
45         uint32_t        base_offset;
46
47         uint32_t        gate_offset;
48         uint32_t        gate_shift;
49
50         uint32_t        mode_reg;
51         uint32_t        mode_val;
52
53         uint32_t        flags;
54
55         struct rk_clk_pll_rate  *rates;
56         struct rk_clk_pll_rate  *frac_rates;
57 };
58
59 #define WRITE4(_clk, off, val)                                  \
60         CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
61 #define READ4(_clk, off, val)                                   \
62         CLKDEV_READ_4(clknode_get_device(_clk), off, val)
63 #define DEVICE_LOCK(_clk)                                       \
64         CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
65 #define DEVICE_UNLOCK(_clk)                                     \
66         CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
67
68 #define RK_CLK_PLL_FBDIV_OFFSET         0
69 #define RK_CLK_PLL_FBDIV_SHIFT          0
70 #define RK_CLK_PLL_FBDIV_MASK           0xFFF
71
72 #define RK_CLK_PLL_POSTDIV1_OFFSET      0
73 #define RK_CLK_PLL_POSTDIV1_SHIFT       12
74 #define RK_CLK_PLL_POSTDIV1_MASK        0x7000
75
76 #define RK_CLK_PLL_DSMPD_OFFSET         4
77 #define RK_CLK_PLL_DSMPD_SHIFT          12
78 #define RK_CLK_PLL_DSMPD_MASK           0x1000
79
80 #define RK_CLK_PLL_REFDIV_OFFSET        4
81 #define RK_CLK_PLL_REFDIV_SHIFT         0
82 #define RK_CLK_PLL_REFDIV_MASK          0x3F
83
84 #define RK_CLK_PLL_POSTDIV2_OFFSET      4
85 #define RK_CLK_PLL_POSTDIV2_SHIFT       6
86 #define RK_CLK_PLL_POSTDIV2_MASK        0x1C0
87
88 #define RK_CLK_PLL_FRAC_OFFSET          8
89 #define RK_CLK_PLL_FRAC_SHIFT           0
90 #define RK_CLK_PLL_FRAC_MASK            0xFFFFFF
91
92 #define RK_CLK_PLL_LOCK_MASK            0x400
93
94 #define RK_CLK_PLL_WRITE_MASK           0xFFFF0000
95
96 static int
97 rk_clk_pll_init(struct clknode *clk, device_t dev)
98 {
99         struct rk_clk_pll_sc *sc;
100
101         sc = clknode_get_softc(clk);
102
103         clknode_init_parent_idx(clk, 0);
104
105         return (0);
106 }
107
108 static int
109 rk_clk_pll_set_gate(struct clknode *clk, bool enable)
110 {
111         struct rk_clk_pll_sc *sc;
112         uint32_t val;
113
114         sc = clknode_get_softc(clk);
115
116         if ((sc->flags & RK_CLK_PLL_HAVE_GATE) == 0)
117                 return (0);
118
119         DEVICE_LOCK(clk);
120         READ4(clk, sc->gate_offset, &val);
121         if (enable)
122                 val &= ~(1 << sc->gate_shift);
123         else
124                 val |= 1 << sc->gate_shift;
125         WRITE4(clk, sc->gate_offset, val | RK_CLK_PLL_MASK);
126         DEVICE_UNLOCK(clk);
127
128         return (0);
129 }
130
131 static int
132 rk_clk_pll_recalc(struct clknode *clk, uint64_t *freq)
133 {
134         struct rk_clk_pll_sc *sc;
135         uint64_t rate;
136         uint32_t dsmpd, refdiv, fbdiv;
137         uint32_t postdiv1, postdiv2, frac;
138         uint32_t raw1, raw2, raw3;
139
140         sc = clknode_get_softc(clk);
141
142         DEVICE_LOCK(clk);
143
144         READ4(clk, sc->base_offset, &raw1);
145         READ4(clk, sc->base_offset + 4, &raw2);
146         READ4(clk, sc->base_offset + 8, &raw3);
147
148         fbdiv = (raw1 & RK_CLK_PLL_FBDIV_MASK) >> RK_CLK_PLL_FBDIV_SHIFT;
149         postdiv1 = (raw1 & RK_CLK_PLL_POSTDIV1_MASK) >> RK_CLK_PLL_POSTDIV1_SHIFT;
150
151         dsmpd = (raw2 & RK_CLK_PLL_DSMPD_MASK) >> RK_CLK_PLL_DSMPD_SHIFT;
152         refdiv = (raw2 & RK_CLK_PLL_REFDIV_MASK) >> RK_CLK_PLL_REFDIV_SHIFT;
153         postdiv2 = (raw2 & RK_CLK_PLL_POSTDIV2_MASK) >> RK_CLK_PLL_POSTDIV2_SHIFT;
154
155         frac = (raw3 & RK_CLK_PLL_FRAC_MASK) >> RK_CLK_PLL_FRAC_SHIFT;
156
157         DEVICE_UNLOCK(clk);
158
159         rate = *freq * fbdiv / refdiv;
160         if (dsmpd == 0) {
161                 /* Fractional mode */
162                 uint64_t frac_rate;
163
164                 frac_rate = *freq * frac / refdiv;
165                 rate += frac_rate >> 24;
166         }
167
168         *freq = rate / postdiv1 / postdiv2;
169
170         if (*freq % 2)
171                 *freq = *freq + 1;
172
173         return (0);
174 }
175
176 static int
177 rk_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
178     int flags, int *stop)
179 {
180         struct rk_clk_pll_rate *rates;
181         struct rk_clk_pll_sc *sc;
182         uint32_t reg;
183         int timeout;
184
185         sc = clknode_get_softc(clk);
186
187         if (sc->rates)
188                 rates = sc->rates;
189         else if (sc->frac_rates)
190                 rates = sc->frac_rates;
191         else
192                 return (EINVAL);
193
194         for (; rates->freq; rates++) {
195                 if (rates->freq == *fout)
196                         break;
197         }
198         if (rates->freq == 0) {
199                 *stop = 1;
200                 return (EINVAL);
201         }
202
203         DEVICE_LOCK(clk);
204
205         /* Setting postdiv1 and fbdiv */
206         READ4(clk, sc->base_offset, &reg);
207         reg &= ~(RK_CLK_PLL_POSTDIV1_MASK | RK_CLK_PLL_FBDIV_MASK);
208         reg |= rates->postdiv1 << RK_CLK_PLL_POSTDIV1_SHIFT;
209         reg |= rates->fbdiv << RK_CLK_PLL_FBDIV_SHIFT;
210         WRITE4(clk, sc->base_offset, reg | RK_CLK_PLL_WRITE_MASK);
211
212         /* Setting dsmpd, postdiv2 and refdiv */
213         READ4(clk, sc->base_offset + 0x4, &reg);
214         reg &= ~(RK_CLK_PLL_DSMPD_MASK | RK_CLK_PLL_POSTDIV2_MASK |
215             RK_CLK_PLL_REFDIV_MASK);
216         reg |= rates->dsmpd << RK_CLK_PLL_DSMPD_SHIFT;
217         reg |= rates->postdiv2 << RK_CLK_PLL_POSTDIV2_SHIFT;
218         reg |= rates->refdiv << RK_CLK_PLL_REFDIV_SHIFT;
219         WRITE4(clk, sc->base_offset + 0x4, reg | RK_CLK_PLL_WRITE_MASK);
220
221         /* Setting frac */
222         READ4(clk, sc->base_offset + 0x8, &reg);
223         reg &= ~RK_CLK_PLL_FRAC_MASK;
224         reg |= rates->frac << RK_CLK_PLL_FRAC_SHIFT;
225         WRITE4(clk, sc->base_offset + 0x8, reg);
226
227         /* Setting to normal mode */
228         READ4(clk, sc->mode_reg, &reg);
229         reg |= sc->mode_val << 16 | sc->mode_val;
230         WRITE4(clk, sc->mode_reg, reg);
231
232         /* Reading lock */
233         for (timeout = 1000; timeout; timeout--) {
234                 READ4(clk, sc->base_offset + 0x4, &reg);
235                 if ((reg & RK_CLK_PLL_LOCK_MASK) == 0)
236                         break;
237                 DELAY(1);
238         }
239
240         DEVICE_UNLOCK(clk);
241
242         *stop = 1;
243         return (0);
244 }
245
246 static clknode_method_t rk_clk_pll_clknode_methods[] = {
247         /* Device interface */
248         CLKNODEMETHOD(clknode_init,             rk_clk_pll_init),
249         CLKNODEMETHOD(clknode_set_gate,         rk_clk_pll_set_gate),
250         CLKNODEMETHOD(clknode_recalc_freq,      rk_clk_pll_recalc),
251         CLKNODEMETHOD(clknode_set_freq,         rk_clk_pll_set_freq),
252         CLKNODEMETHOD_END
253 };
254
255 DEFINE_CLASS_1(rk_clk_pll_clknode, rk_clk_pll_clknode_class,
256     rk_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class);
257
258 int
259 rk_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef)
260 {
261         struct clknode *clk;
262         struct rk_clk_pll_sc *sc;
263
264         clk = clknode_create(clkdom, &rk_clk_pll_clknode_class,
265             &clkdef->clkdef);
266         if (clk == NULL)
267                 return (1);
268
269         sc = clknode_get_softc(clk);
270
271         sc->base_offset = clkdef->base_offset;
272         sc->gate_offset = clkdef->gate_offset;
273         sc->gate_shift = clkdef->gate_shift;
274         sc->mode_reg = clkdef->mode_reg;
275         sc->mode_val = clkdef->mode_val;
276         sc->flags = clkdef->flags;
277         sc->rates = clkdef->rates;
278         sc->frac_rates = clkdef->frac_rates;
279
280         clknode_register(clkdom, clk);
281
282         return (0);
283 }