]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/rockchip/clk/rk_clk_composite.c
rk3328: Add support for this SoC
[FreeBSD/FreeBSD.git] / sys / arm64 / rockchip / clk / rk_clk_composite.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_composite.h>
41
42 #include "clkdev_if.h"
43
44 struct rk_clk_composite_sc {
45         uint32_t        muxdiv_offset;
46         uint32_t        mux_shift;
47         uint32_t        mux_width;
48         uint32_t        mux_mask;
49
50         uint32_t        div_shift;
51         uint32_t        div_width;
52         uint32_t        div_mask;
53
54         uint32_t        gate_offset;
55         uint32_t        gate_shift;
56
57         uint32_t        flags;
58 };
59
60 #define WRITE4(_clk, off, val)                                          \
61         CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
62 #define READ4(_clk, off, val)                                           \
63         CLKDEV_READ_4(clknode_get_device(_clk), off, val)
64 #define DEVICE_LOCK(_clk)                                                       \
65         CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
66 #define DEVICE_UNLOCK(_clk)                                             \
67         CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
68
69 #define RK_COMPOSITE_WRITE_MASK 0xFFFF0000
70
71 static int
72 rk_clk_composite_init(struct clknode *clk, device_t dev)
73 {
74         struct rk_clk_composite_sc *sc;
75         uint32_t val, idx;
76
77         sc = clknode_get_softc(clk);
78
79         idx = 0;
80         if ((sc->flags & RK_CLK_COMPOSITE_HAVE_MUX) != 0) {
81                 DEVICE_LOCK(clk);
82                 READ4(clk, sc->muxdiv_offset, &val);
83                 DEVICE_UNLOCK(clk);
84
85                 idx = (val & sc->mux_mask) >> sc->mux_shift;
86         }
87
88         clknode_init_parent_idx(clk, idx);
89
90         return (0);
91 }
92
93 static int
94 rk_clk_composite_set_gate(struct clknode *clk, bool enable)
95 {
96         struct rk_clk_composite_sc *sc;
97         uint32_t val;
98
99         sc = clknode_get_softc(clk);
100
101         if ((sc->flags & RK_CLK_COMPOSITE_HAVE_GATE) == 0)
102                 return (0);
103
104         DEVICE_LOCK(clk);
105         READ4(clk, sc->gate_offset, &val);
106         if (enable)
107                 val &= ~(1 << sc->gate_shift);
108         else
109                 val |= 1 << sc->gate_shift;
110         WRITE4(clk, sc->gate_offset, val | RK_CLK_COMPOSITE_MASK);
111         DEVICE_UNLOCK(clk);
112
113         return (0);
114 }
115
116 static int
117 rk_clk_composite_set_mux(struct clknode *clk, int index)
118 {
119         struct rk_clk_composite_sc *sc;
120         uint32_t val;
121
122         sc = clknode_get_softc(clk);
123
124         if ((sc->flags & RK_CLK_COMPOSITE_HAVE_MUX) == 0)
125                 return (0);
126
127         DEVICE_LOCK(clk);
128         READ4(clk, sc->muxdiv_offset, &val);
129         val &= ~(sc->mux_mask >> sc->mux_shift);
130         val |= index << sc->mux_shift;
131         WRITE4(clk, sc->muxdiv_offset, val);
132         DEVICE_UNLOCK(clk);
133
134         return (0);
135 }
136
137 static int
138 rk_clk_composite_recalc(struct clknode *clk, uint64_t *freq)
139 {
140         struct rk_clk_composite_sc *sc;
141         uint32_t reg, div;
142
143         sc = clknode_get_softc(clk);
144
145         DEVICE_LOCK(clk);
146
147         READ4(clk, sc->muxdiv_offset, &reg);
148
149         DEVICE_UNLOCK(clk);
150
151         div = ((reg & sc->div_mask) >> sc->div_shift) + 1;
152
153         *freq = *freq / div;
154
155         return (0);
156 }
157
158 static int
159 rk_clk_composite_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
160     int flags, int *stop)
161 {
162         struct rk_clk_composite_sc *sc;
163         uint64_t best, cur;
164         uint32_t div, best_div, val;
165
166         sc = clknode_get_softc(clk);
167
168         for (best = 0, best_div = 0, div = 0; div <= sc->div_mask; div++) {
169                 cur = fparent / div;
170                 if ((*fout - cur) < (*fout - best)) {
171                         best = cur;
172                         best_div = div;
173                         break;
174                 }
175         }
176
177         if (best_div == 0)
178                 return (0);
179
180         if ((best < *fout) &&
181           ((flags & CLK_SET_ROUND_DOWN) == 0)) {
182                 *stop = 1;
183                 return (ERANGE);
184         }
185         if ((best > *fout) &&
186           ((flags & CLK_SET_ROUND_UP) == 0)) {
187                 *stop = 1;
188                 return (ERANGE);
189         }
190
191         if ((flags & CLK_SET_DRYRUN) != 0) {
192                 *fout = best;
193                 *stop = 1;
194                 return (0);
195         }
196
197         DEVICE_LOCK(clk);
198         READ4(clk, sc->muxdiv_offset, &val);
199         val &= ~sc->div_mask;
200         val |= (best_div - 1) << sc->div_shift;
201         WRITE4(clk, sc->muxdiv_offset, val | RK_CLK_COMPOSITE_MASK);
202         DEVICE_UNLOCK(clk);
203
204         *fout = best;
205         *stop = 1;
206
207         return (0);
208 }
209
210 static clknode_method_t rk_clk_composite_clknode_methods[] = {
211         /* Device interface */
212         CLKNODEMETHOD(clknode_init,             rk_clk_composite_init),
213         CLKNODEMETHOD(clknode_set_gate,         rk_clk_composite_set_gate),
214         CLKNODEMETHOD(clknode_set_mux,          rk_clk_composite_set_mux),
215         CLKNODEMETHOD(clknode_recalc_freq,      rk_clk_composite_recalc),
216         CLKNODEMETHOD(clknode_set_freq,         rk_clk_composite_set_freq),
217         CLKNODEMETHOD_END
218 };
219
220 DEFINE_CLASS_1(rk_clk_composite_clknode, rk_clk_composite_clknode_class,
221     rk_clk_composite_clknode_methods, sizeof(struct rk_clk_composite_sc),
222     clknode_class);
223
224 int
225 rk_clk_composite_register(struct clkdom *clkdom, struct rk_clk_composite_def *clkdef)
226 {
227         struct clknode *clk;
228         struct rk_clk_composite_sc *sc;
229
230         clk = clknode_create(clkdom, &rk_clk_composite_clknode_class,
231             &clkdef->clkdef);
232         if (clk == NULL)
233                 return (1);
234
235         sc = clknode_get_softc(clk);
236
237         sc->muxdiv_offset = clkdef->muxdiv_offset;
238
239         sc->mux_shift = clkdef->mux_shift;
240         sc->mux_width = clkdef->mux_width;
241         sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
242
243         sc->div_shift = clkdef->div_shift;
244         sc->div_width = clkdef->div_width;
245         sc->div_mask = ((1 << clkdef->div_width) - 1) << sc->div_shift;
246
247         sc->gate_offset = clkdef->gate_offset;
248         sc->gate_shift = clkdef->gate_shift;
249
250         sc->flags = clkdef->flags;
251
252         clknode_register(clkdom, clk);
253
254         return (0);
255 }