xref: /freebsd/sys/dev/clk/starfive/jh7110_clk.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
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
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
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
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
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
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
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
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
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