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