1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
5 * Copyright (c) 2022 Mitchell Horne <mhorne@FreeBSD.org>
6 * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com>
7 */
8
9 #include <sys/param.h>
10 #include <sys/systm.h>
11 #include <sys/bus.h>
12 #include <sys/mutex.h>
13 #include <sys/rman.h>
14
15 #include <machine/bus.h>
16 #include <machine/intr.h>
17 #include <machine/resource.h>
18
19 #include <dev/clk/clk.h>
20 #include <dev/hwreset/hwreset.h>
21
22 #include <dt-bindings/clock/starfive,jh7110-crg.h>
23
24 #include <dev/clk/starfive/jh7110_clk.h>
25
26 #include "clkdev_if.h"
27 #include "hwreset_if.h"
28
29 #define JH7110_DIV_MASK 0xffffff
30 #define JH7110_MUX_SHIFT 24
31 #define JH7110_MUX_MASK 0x3f000000
32 #define JH7110_ENABLE_SHIFT 31
33
34 #define REG_SIZE 4
35
36 struct jh7110_clk_sc {
37 uint32_t offset;
38 uint32_t flags;
39 uint64_t d_max;
40 int id;
41 };
42
43 #define DIV_ROUND_CLOSEST(n, d) (((n) + (d) / 2) / (d))
44
45 #define READ4(_sc, _off) \
46 bus_read_4(_sc->mem_res, _off)
47 #define WRITE4(_sc, _off, _val) \
48 bus_write_4(_sc->mem_res, _off, _val)
49
50 #define DEVICE_LOCK(_clk) \
51 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
52 #define DEVICE_UNLOCK(_clk) \
53 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
54
55 /* Reset functions */
56
57 int
jh7110_reset_assert(device_t dev,intptr_t id,bool assert)58 jh7110_reset_assert(device_t dev, intptr_t id, bool assert)
59 {
60 struct jh7110_clkgen_softc *sc;
61 uint32_t regvalue, offset, bitmask = 1UL << id % 32;
62
63 sc = device_get_softc(dev);
64 offset = sc->reset_selector_offset + id / 32 * 4;
65
66 mtx_lock(&sc->mtx);
67
68 regvalue = READ4(sc, offset);
69
70 if (assert)
71 regvalue |= bitmask;
72 else
73 regvalue &= ~bitmask;
74 WRITE4(sc, offset, regvalue);
75
76 mtx_unlock(&sc->mtx);
77
78 return (0);
79 }
80
81 int
jh7110_reset_is_asserted(device_t dev,intptr_t id,bool * reset)82 jh7110_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
83 {
84 struct jh7110_clkgen_softc *sc;
85 uint32_t regvalue, offset, bitmask;
86
87 sc = device_get_softc(dev);
88 offset = sc->reset_status_offset + id / 32 * 4;
89
90 mtx_lock(&sc->mtx);
91
92 regvalue = READ4(sc, offset);
93 bitmask = 1UL << id % 32;
94
95 mtx_unlock(&sc->mtx);
96
97 *reset = (regvalue & bitmask) == 0;
98
99 return (0);
100 }
101
102 /* Clock functions */
103
104 static int
jh7110_clk_init(struct clknode * clk,device_t dev)105 jh7110_clk_init(struct clknode *clk, device_t dev)
106 {
107 struct jh7110_clkgen_softc *sc;
108 struct jh7110_clk_sc *sc_clk;
109 uint32_t reg;
110 int idx = 0;
111
112 sc = device_get_softc(clknode_get_device(clk));
113 sc_clk = clknode_get_softc(clk);
114
115 if (sc_clk->flags & JH7110_CLK_HAS_MUX) {
116 DEVICE_LOCK(clk);
117 reg = READ4(sc, sc_clk->offset);
118 DEVICE_UNLOCK(clk);
119 idx = (reg & JH7110_MUX_MASK) >> JH7110_MUX_SHIFT;
120 }
121
122 clknode_init_parent_idx(clk, idx);
123
124 return (0);
125 }
126
127 static int
jh7110_clk_set_gate(struct clknode * clk,bool enable)128 jh7110_clk_set_gate(struct clknode *clk, bool enable)
129 {
130 struct jh7110_clkgen_softc *sc;
131 struct jh7110_clk_sc *sc_clk;
132 uint32_t reg;
133
134 sc = device_get_softc(clknode_get_device(clk));
135 sc_clk = clknode_get_softc(clk);
136
137 if ((sc_clk->flags & JH7110_CLK_HAS_GATE) == 0)
138 return (0);
139
140 DEVICE_LOCK(clk);
141
142 reg = READ4(sc, sc_clk->offset);
143 if (enable)
144 reg |= (1 << JH7110_ENABLE_SHIFT);
145 else
146 reg &= ~(1 << JH7110_ENABLE_SHIFT);
147 WRITE4(sc, sc_clk->offset, reg);
148
149 DEVICE_UNLOCK(clk);
150
151 return (0);
152 }
153
154 static int
jh7110_clk_set_mux(struct clknode * clk,int idx)155 jh7110_clk_set_mux(struct clknode *clk, int idx)
156 {
157 struct jh7110_clkgen_softc *sc;
158 struct jh7110_clk_sc *sc_clk;
159 uint32_t reg;
160
161 sc = device_get_softc(clknode_get_device(clk));
162 sc_clk = clknode_get_softc(clk);
163
164 if ((sc_clk->flags & JH7110_CLK_HAS_MUX) == 0)
165 return (ENXIO);
166
167 /* Checking index size */
168 if ((idx & (JH7110_MUX_MASK >> JH7110_MUX_SHIFT)) != idx)
169 return (EINVAL);
170
171 DEVICE_LOCK(clk);
172
173 reg = READ4(sc, sc_clk->offset) & ~JH7110_MUX_MASK;
174 reg |= idx << JH7110_MUX_SHIFT;
175 WRITE4(sc, sc_clk->offset, reg);
176
177 DEVICE_UNLOCK(clk);
178
179 return (0);
180 }
181
182 static int
jh7110_clk_recalc_freq(struct clknode * clk,uint64_t * freq)183 jh7110_clk_recalc_freq(struct clknode *clk, uint64_t *freq)
184 {
185 struct jh7110_clkgen_softc *sc;
186 struct jh7110_clk_sc *sc_clk;
187 uint32_t divisor;
188
189 sc = device_get_softc(clknode_get_device(clk));
190 sc_clk = clknode_get_softc(clk);
191
192 /* Returning error here causes panic */
193 if ((sc_clk->flags & JH7110_CLK_HAS_DIV) == 0)
194 return (0);
195
196 DEVICE_LOCK(clk);
197
198 divisor = READ4(sc, sc_clk->offset) & JH7110_DIV_MASK;
199
200 DEVICE_UNLOCK(clk);
201
202 if (divisor)
203 *freq = *freq / divisor;
204 else
205 *freq = 0;
206
207 return (0);
208 }
209
210 static int
jh7110_clk_set_freq(struct clknode * clk,uint64_t fin,uint64_t * fout,int flags,int * done)211 jh7110_clk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
212 int flags, int *done)
213 {
214 struct jh7110_clkgen_softc *sc;
215 struct jh7110_clk_sc *sc_clk;
216 uint32_t divisor;
217
218 sc = device_get_softc(clknode_get_device(clk));
219 sc_clk = clknode_get_softc(clk);
220
221 if ((sc_clk->flags & JH7110_CLK_HAS_DIV) == 0)
222 return (0);
223
224 divisor = MIN(MAX(DIV_ROUND_CLOSEST(fin, *fout), 1UL), sc_clk->d_max);
225
226 if (flags & CLK_SET_DRYRUN)
227 goto done;
228
229 DEVICE_LOCK(clk);
230
231 divisor |= READ4(sc, sc_clk->offset) & ~JH7110_DIV_MASK;
232 WRITE4(sc, sc_clk->offset, divisor);
233
234 DEVICE_UNLOCK(clk);
235
236 done:
237 *fout = divisor;
238 *done = 1;
239
240 return (0);
241 }
242
243 static clknode_method_t jh7110_clknode_methods[] = {
244 /* Device interface */
245 CLKNODEMETHOD(clknode_init, jh7110_clk_init),
246 CLKNODEMETHOD(clknode_set_gate, jh7110_clk_set_gate),
247 CLKNODEMETHOD(clknode_set_mux, jh7110_clk_set_mux),
248 CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_recalc_freq),
249 CLKNODEMETHOD(clknode_set_freq, jh7110_clk_set_freq),
250 CLKNODEMETHOD_END
251 };
252
253 DEFINE_CLASS_1(jh7110_clknode, jh7110_clknode_class, jh7110_clknode_methods,
254 sizeof(struct jh7110_clk_sc), clknode_class);
255
256 int
jh7110_clk_register(struct clkdom * clkdom,const struct jh7110_clk_def * clkdef)257 jh7110_clk_register(struct clkdom *clkdom, const struct jh7110_clk_def *clkdef)
258 {
259 struct clknode *clk;
260 struct jh7110_clk_sc *sc;
261
262 clk = clknode_create(clkdom, &jh7110_clknode_class, &clkdef->clkdef);
263 if (clk == NULL)
264 return (-1);
265
266 sc = clknode_get_softc(clk);
267
268 sc->offset = clkdef->clkdef.id * REG_SIZE;
269
270 sc->flags = clkdef->flags;
271 sc->id = clkdef->clkdef.id;
272 sc->d_max = clkdef->d_max;
273
274 clknode_register(clkdom, clk);
275
276 return (0);
277 }
278