xref: /freebsd/sys/dev/clk/spacemit/k1_clk.c (revision dcb10e3add17259715352885dfc55ca92061d2ed)
1*dcb10e3aSBojan Novković /*
2*dcb10e3aSBojan Novković  * Copyright (c) 2026 Bojan Novković <bnovkov@FreeBSD.org>
3*dcb10e3aSBojan Novković  *
4*dcb10e3aSBojan Novković  * SPDX-License-Identifier: BSD-2-Clause
5*dcb10e3aSBojan Novković  */
6*dcb10e3aSBojan Novković 
7*dcb10e3aSBojan Novković /*
8*dcb10e3aSBojan Novković  * Generic clock routines for the SpacemiT K1 SoC.
9*dcb10e3aSBojan Novković  *
10*dcb10e3aSBojan Novković  * Written using SpacemiT's K1 Chip Product Documentation,
11*dcb10e3aSBojan Novković  * Section 9., "Top System (1/2)", available at
12*dcb10e3aSBojan Novković  * https://www.spacemit.com/community/document/
13*dcb10e3aSBojan Novković  */
14*dcb10e3aSBojan Novković 
15*dcb10e3aSBojan Novković #include <sys/cdefs.h>
16*dcb10e3aSBojan Novković 
17*dcb10e3aSBojan Novković #include <sys/param.h>
18*dcb10e3aSBojan Novković #include <sys/systm.h>
19*dcb10e3aSBojan Novković #include <sys/bus.h>
20*dcb10e3aSBojan Novković #include <sys/kernel.h>
21*dcb10e3aSBojan Novković #include <sys/module.h>
22*dcb10e3aSBojan Novković #include <sys/mutex.h>
23*dcb10e3aSBojan Novković 
24*dcb10e3aSBojan Novković #include <machine/bus.h>
25*dcb10e3aSBojan Novković #include <sys/rman.h>
26*dcb10e3aSBojan Novković #include <machine/resource.h>
27*dcb10e3aSBojan Novković 
28*dcb10e3aSBojan Novković #include <dev/fdt/simplebus.h>
29*dcb10e3aSBojan Novković 
30*dcb10e3aSBojan Novković #include <dev/ofw/ofw_bus.h>
31*dcb10e3aSBojan Novković #include <dev/ofw/ofw_bus_subr.h>
32*dcb10e3aSBojan Novković 
33*dcb10e3aSBojan Novković #include <dev/clk/clk.h>
34*dcb10e3aSBojan Novković 
35*dcb10e3aSBojan Novković #include <dev/hwreset/hwreset.h>
36*dcb10e3aSBojan Novković 
37*dcb10e3aSBojan Novković #include "k1_clk.h"
38*dcb10e3aSBojan Novković #include "clkdev_if.h"
39*dcb10e3aSBojan Novković #include "hwreset_if.h"
40*dcb10e3aSBojan Novković 
41*dcb10e3aSBojan Novković #define K1_CLK_FCREQ_NRETRIES 5
42*dcb10e3aSBojan Novković 
43*dcb10e3aSBojan Novković #define DEVICE_LOCK(_clk)				\
44*dcb10e3aSBojan Novković 	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
45*dcb10e3aSBojan Novković #define DEVICE_UNLOCK(_clk)				\
46*dcb10e3aSBojan Novković 	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
47*dcb10e3aSBojan Novković 
48*dcb10e3aSBojan Novković #define READ4(sc, reg)		bus_read_4((sc)->res, (reg))
49*dcb10e3aSBojan Novković #define WRITE4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
50*dcb10e3aSBojan Novković 
51*dcb10e3aSBojan Novković static int
k1_clk_register(struct clkdom * clkdom,const struct k1_clk_def * clkdef,clknode_class_t class)52*dcb10e3aSBojan Novković k1_clk_register(struct clkdom *clkdom,
53*dcb10e3aSBojan Novković     const struct k1_clk_def *clkdef, clknode_class_t class)
54*dcb10e3aSBojan Novković {
55*dcb10e3aSBojan Novković 	struct k1_clk_def *sc;
56*dcb10e3aSBojan Novković 	struct clknode *clk;
57*dcb10e3aSBojan Novković 
58*dcb10e3aSBojan Novković 	clk = clknode_create(clkdom, class,
59*dcb10e3aSBojan Novković 	    &clkdef->clkdef);
60*dcb10e3aSBojan Novković 	if (clk == NULL)
61*dcb10e3aSBojan Novković 		return (1);
62*dcb10e3aSBojan Novković 
63*dcb10e3aSBojan Novković 	sc = clknode_get_softc(clk);
64*dcb10e3aSBojan Novković 	*sc = *clkdef;
65*dcb10e3aSBojan Novković 	clknode_register(clkdom, clk);
66*dcb10e3aSBojan Novković 
67*dcb10e3aSBojan Novković 	return (0);
68*dcb10e3aSBojan Novković }
69*dcb10e3aSBojan Novković 
70*dcb10e3aSBojan Novković static void
k1_clkdev_lock(device_t dev)71*dcb10e3aSBojan Novković k1_clkdev_lock(device_t dev)
72*dcb10e3aSBojan Novković {
73*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
74*dcb10e3aSBojan Novković 
75*dcb10e3aSBojan Novković 	sc = device_get_softc(dev);
76*dcb10e3aSBojan Novković 	mtx_lock(&sc->mtx);
77*dcb10e3aSBojan Novković }
78*dcb10e3aSBojan Novković 
79*dcb10e3aSBojan Novković static void
k1_clkdev_unlock(device_t dev)80*dcb10e3aSBojan Novković k1_clkdev_unlock(device_t dev)
81*dcb10e3aSBojan Novković {
82*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
83*dcb10e3aSBojan Novković 
84*dcb10e3aSBojan Novković 	sc = device_get_softc(dev);
85*dcb10e3aSBojan Novković 	mtx_unlock(&sc->mtx);
86*dcb10e3aSBojan Novković }
87*dcb10e3aSBojan Novković 
88*dcb10e3aSBojan Novković int
k1_clk_attach(device_t dev)89*dcb10e3aSBojan Novković k1_clk_attach(device_t dev)
90*dcb10e3aSBojan Novković {
91*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
92*dcb10e3aSBojan Novković 	clknode_class_t class;
93*dcb10e3aSBojan Novković 	int error, rid, i;
94*dcb10e3aSBojan Novković 
95*dcb10e3aSBojan Novković 	sc = device_get_softc(dev);
96*dcb10e3aSBojan Novković 	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
97*dcb10e3aSBojan Novković 
98*dcb10e3aSBojan Novković 	if (sc->resets != NULL)
99*dcb10e3aSBojan Novković 		hwreset_register_ofw_provider(dev);
100*dcb10e3aSBojan Novković 
101*dcb10e3aSBojan Novković 	sc->clkdom = clkdom_create(dev);
102*dcb10e3aSBojan Novković 	if (sc->clkdom == NULL) {
103*dcb10e3aSBojan Novković 		device_printf(dev, "Couldn't create clkdom\n");
104*dcb10e3aSBojan Novković 		return (ENXIO);
105*dcb10e3aSBojan Novković 	}
106*dcb10e3aSBojan Novković 
107*dcb10e3aSBojan Novković 	rid = 0;
108*dcb10e3aSBojan Novković 	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
109*dcb10e3aSBojan Novković 	    &rid, RF_ACTIVE);
110*dcb10e3aSBojan Novković 	if (sc->res == NULL) {
111*dcb10e3aSBojan Novković 		device_printf(dev, "Can't allocate memory\n");
112*dcb10e3aSBojan Novković 		return (ENXIO);
113*dcb10e3aSBojan Novković 	}
114*dcb10e3aSBojan Novković 
115*dcb10e3aSBojan Novković 	class = sc->clknode_class != NULL ?
116*dcb10e3aSBojan Novković 	    sc->clknode_class : &k1_clknode_class;
117*dcb10e3aSBojan Novković 	for (i = 0; i < sc->nclks; i++) {
118*dcb10e3aSBojan Novković 		error = k1_clk_register(sc->clkdom, &sc->clks[i], class);
119*dcb10e3aSBojan Novković 		if (error != 0) {
120*dcb10e3aSBojan Novković 			device_printf(dev, "Failed to register clock '%s'\n",
121*dcb10e3aSBojan Novković 			    sc->clks[i].clkdef.name);
122*dcb10e3aSBojan Novković 			bus_free_resource(dev, SYS_RES_MEMORY, sc->res);
123*dcb10e3aSBojan Novković 			return (error);
124*dcb10e3aSBojan Novković 		}
125*dcb10e3aSBojan Novković 	}
126*dcb10e3aSBojan Novković 
127*dcb10e3aSBojan Novković 	error = clkdom_finit(sc->clkdom);
128*dcb10e3aSBojan Novković 	if (error != 0) {
129*dcb10e3aSBojan Novković 		device_printf(dev, "clkdom_finit() returned %d\n", error);
130*dcb10e3aSBojan Novković 		bus_free_resource(dev, SYS_RES_MEMORY, sc->res);
131*dcb10e3aSBojan Novković 		return (error);
132*dcb10e3aSBojan Novković 	}
133*dcb10e3aSBojan Novković 
134*dcb10e3aSBojan Novković 	if (bootverbose)
135*dcb10e3aSBojan Novković 		clkdom_dump(sc->clkdom);
136*dcb10e3aSBojan Novković 
137*dcb10e3aSBojan Novković 	return (0);
138*dcb10e3aSBojan Novković }
139*dcb10e3aSBojan Novković 
140*dcb10e3aSBojan Novković static int
k1_clkdev_reset_assert(device_t dev,intptr_t id,bool assert)141*dcb10e3aSBojan Novković k1_clkdev_reset_assert(device_t dev, intptr_t id, bool assert)
142*dcb10e3aSBojan Novković {
143*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
144*dcb10e3aSBojan Novković 	const struct k1_reset_def *rdef;
145*dcb10e3aSBojan Novković 	uint32_t val;
146*dcb10e3aSBojan Novković 
147*dcb10e3aSBojan Novković 	sc = device_get_softc(dev);
148*dcb10e3aSBojan Novković 
149*dcb10e3aSBojan Novković 	if (id >= sc->nresets)
150*dcb10e3aSBojan Novković 		return (0);
151*dcb10e3aSBojan Novković 	rdef = &sc->resets[id];
152*dcb10e3aSBojan Novković 	if (rdef->reg == 0)
153*dcb10e3aSBojan Novković 		return (0);
154*dcb10e3aSBojan Novković 
155*dcb10e3aSBojan Novković 	mtx_lock(&sc->mtx);
156*dcb10e3aSBojan Novković 	val = READ4(sc, rdef->reg);
157*dcb10e3aSBojan Novković 	if (assert)
158*dcb10e3aSBojan Novković 		val |= rdef->assert_mask;
159*dcb10e3aSBojan Novković 	else
160*dcb10e3aSBojan Novković 		val &= ~rdef->deassert_mask;
161*dcb10e3aSBojan Novković 	WRITE4(sc, rdef->reg, val);
162*dcb10e3aSBojan Novković 	mtx_unlock(&sc->mtx);
163*dcb10e3aSBojan Novković 
164*dcb10e3aSBojan Novković 	return (0);
165*dcb10e3aSBojan Novković }
166*dcb10e3aSBojan Novković 
167*dcb10e3aSBojan Novković static device_method_t k1_clkdev_methods[] = {
168*dcb10e3aSBojan Novković 	/* clkdev interface */
169*dcb10e3aSBojan Novković 	DEVMETHOD(clkdev_device_lock,	k1_clkdev_lock),
170*dcb10e3aSBojan Novković 	DEVMETHOD(clkdev_device_unlock,	k1_clkdev_unlock),
171*dcb10e3aSBojan Novković 
172*dcb10e3aSBojan Novković 	/* Reset interface */
173*dcb10e3aSBojan Novković 	DEVMETHOD(hwreset_assert,	k1_clkdev_reset_assert),
174*dcb10e3aSBojan Novković 
175*dcb10e3aSBojan Novković 	DEVMETHOD_END
176*dcb10e3aSBojan Novković };
177*dcb10e3aSBojan Novković DEFINE_CLASS_0(k1_clkdev, k1_clkdev_driver, k1_clkdev_methods,
178*dcb10e3aSBojan Novković     sizeof(struct k1_clkdev_softc));
179*dcb10e3aSBojan Novković 
180*dcb10e3aSBojan Novković static int
k1_clk_set_gate(struct clknode * clk,bool enable)181*dcb10e3aSBojan Novković k1_clk_set_gate(struct clknode *clk, bool enable)
182*dcb10e3aSBojan Novković {
183*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
184*dcb10e3aSBojan Novković 	struct k1_clk_def *clk_sc;
185*dcb10e3aSBojan Novković 	uint32_t reg;
186*dcb10e3aSBojan Novković 
187*dcb10e3aSBojan Novković 	sc = device_get_softc(clknode_get_device(clk));
188*dcb10e3aSBojan Novković 	clk_sc = clknode_get_softc(clk);
189*dcb10e3aSBojan Novković 	if (clk_sc->gate_mask == 0 || clk_sc->reg == 0)
190*dcb10e3aSBojan Novković 		return (0);
191*dcb10e3aSBojan Novković 
192*dcb10e3aSBojan Novković 	DEVICE_LOCK(clk);
193*dcb10e3aSBojan Novković 	reg = READ4(sc, clk_sc->reg);
194*dcb10e3aSBojan Novković 	if (enable)
195*dcb10e3aSBojan Novković 		reg |= clk_sc->gate_mask;
196*dcb10e3aSBojan Novković 	else
197*dcb10e3aSBojan Novković 		reg &= ~clk_sc->gate_mask;
198*dcb10e3aSBojan Novković 	WRITE4(sc, clk_sc->reg, reg);
199*dcb10e3aSBojan Novković 	DEVICE_UNLOCK(clk);
200*dcb10e3aSBojan Novković 
201*dcb10e3aSBojan Novković 	return (0);
202*dcb10e3aSBojan Novković }
203*dcb10e3aSBojan Novković 
204*dcb10e3aSBojan Novković static bool
k1_clk_rounding_check(uint64_t curfreq,uint64_t req,int flags)205*dcb10e3aSBojan Novković k1_clk_rounding_check(uint64_t curfreq, uint64_t req, int flags)
206*dcb10e3aSBojan Novković {
207*dcb10e3aSBojan Novković 	switch (CLK_SET_ROUND(flags)) {
208*dcb10e3aSBojan Novković 	case CLK_SET_ROUND_DOWN:
209*dcb10e3aSBojan Novković 		return curfreq <= req;
210*dcb10e3aSBojan Novković 	case CLK_SET_ROUND_UP:
211*dcb10e3aSBojan Novković 		return curfreq >= req;
212*dcb10e3aSBojan Novković 	case CLK_SET_ROUND_EXACT:
213*dcb10e3aSBojan Novković 		/* CLK_SET_ROUND_EXACT is enforced in 'k1_clk_set_freq'. */
214*dcb10e3aSBojan Novković 	case CLK_SET_ROUND_ANY:
215*dcb10e3aSBojan Novković 	default:
216*dcb10e3aSBojan Novković 		return true;
217*dcb10e3aSBojan Novković 	}
218*dcb10e3aSBojan Novković }
219*dcb10e3aSBojan Novković 
220*dcb10e3aSBojan Novković /*
221*dcb10e3aSBojan Novković  * Find the closest matching rate given the requested frequency 'req' and
222*dcb10e3aSBojan Novković  * rounding mode in 'flags' by going through all possible combinations of
223*dcb10e3aSBojan Novković  * frequency divisor values and parent clock rates.
224*dcb10e3aSBojan Novković  */
225*dcb10e3aSBojan Novković static uint64_t
k1_clk_find_best_rate(struct clknode * clk,struct k1_clk_def * sc,uint64_t req,int flags,int * parentp,uint32_t * divp)226*dcb10e3aSBojan Novković k1_clk_find_best_rate(struct clknode *clk, struct k1_clk_def *sc, uint64_t req,
227*dcb10e3aSBojan Novković     int flags, int *parentp, uint32_t *divp)
228*dcb10e3aSBojan Novković {
229*dcb10e3aSBojan Novković 	uint64_t best_match, best_delta, p_freq, candidate_freq;
230*dcb10e3aSBojan Novković 	uint32_t i, divmax, best_div;
231*dcb10e3aSBojan Novković 	struct clknode *p_clk;
232*dcb10e3aSBojan Novković 	const char **p_names;
233*dcb10e3aSBojan Novković 	int best_parent;
234*dcb10e3aSBojan Novković 	int p_idx;
235*dcb10e3aSBojan Novković 
236*dcb10e3aSBojan Novković 	best_div = 1;
237*dcb10e3aSBojan Novković 	best_match = 0;
238*dcb10e3aSBojan Novković 	best_parent = 0;
239*dcb10e3aSBojan Novković 	best_delta = UINT64_MAX;
240*dcb10e3aSBojan Novković 	p_names = clknode_get_parent_names(clk);
241*dcb10e3aSBojan Novković 	for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {
242*dcb10e3aSBojan Novković 		p_clk = clknode_find_by_name(p_names[p_idx]);
243*dcb10e3aSBojan Novković 		clknode_get_freq(p_clk, &p_freq);
244*dcb10e3aSBojan Novković 
245*dcb10e3aSBojan Novković 		divmax = 1 << sc->div_nbits;
246*dcb10e3aSBojan Novković 		for (i = 1; i <= divmax; i++) {
247*dcb10e3aSBojan Novković 			candidate_freq = p_freq / i;
248*dcb10e3aSBojan Novković 			if (clk_freq_diff(req, candidate_freq) < best_delta &&
249*dcb10e3aSBojan Novković 			    k1_clk_rounding_check(candidate_freq, req, flags)) {
250*dcb10e3aSBojan Novković 				best_match = candidate_freq;
251*dcb10e3aSBojan Novković 				best_parent = p_idx;
252*dcb10e3aSBojan Novković 				best_delta = clk_freq_diff(req, candidate_freq);
253*dcb10e3aSBojan Novković 				best_div = i;
254*dcb10e3aSBojan Novković 			}
255*dcb10e3aSBojan Novković 		}
256*dcb10e3aSBojan Novković 	}
257*dcb10e3aSBojan Novković 
258*dcb10e3aSBojan Novković 	*parentp = best_parent;
259*dcb10e3aSBojan Novković 	*divp = best_div;
260*dcb10e3aSBojan Novković 
261*dcb10e3aSBojan Novković 	return (best_match);
262*dcb10e3aSBojan Novković }
263*dcb10e3aSBojan Novković 
264*dcb10e3aSBojan Novković /*
265*dcb10e3aSBojan Novković  * Certain clocks must perform a "frequency change request" after
266*dcb10e3aSBojan Novković  * reparenting or changing the clock's frequency divisor. This is done
267*dcb10e3aSBojan Novković  * by setting the appropriate FC bit in the clock's configuration
268*dcb10e3aSBojan Novković  * register and waiting until the hardware clears it, indicating
269*dcb10e3aSBojan Novković  * that the clock's divisor or parent index was successfully updated.
270*dcb10e3aSBojan Novković  */
271*dcb10e3aSBojan Novković static int
k1_clk_send_fc_req(struct clknode * clk)272*dcb10e3aSBojan Novković k1_clk_send_fc_req(struct clknode *clk)
273*dcb10e3aSBojan Novković {
274*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
275*dcb10e3aSBojan Novković 	struct k1_clk_def *clk_sc;
276*dcb10e3aSBojan Novković 	uint32_t val;
277*dcb10e3aSBojan Novković 	int retries;
278*dcb10e3aSBojan Novković 
279*dcb10e3aSBojan Novković 	sc = device_get_softc(clknode_get_device(clk));
280*dcb10e3aSBojan Novković 	clk_sc = clknode_get_softc(clk);
281*dcb10e3aSBojan Novković 
282*dcb10e3aSBojan Novković 	if (clk_sc->fc_mask == 0)
283*dcb10e3aSBojan Novković 		return (0);
284*dcb10e3aSBojan Novković 
285*dcb10e3aSBojan Novković 	retries = K1_CLK_FCREQ_NRETRIES;
286*dcb10e3aSBojan Novković 	val = READ4(sc, clk_sc->reg);
287*dcb10e3aSBojan Novković 	val |= clk_sc->fc_mask;
288*dcb10e3aSBojan Novković 	WRITE4(sc, clk_sc->reg, val);
289*dcb10e3aSBojan Novković 	while (retries-- > 0) {
290*dcb10e3aSBojan Novković 		DELAY(1000);
291*dcb10e3aSBojan Novković 		val = READ4(sc, clk_sc->reg);
292*dcb10e3aSBojan Novković 		if ((val & clk_sc->fc_mask) == 0)
293*dcb10e3aSBojan Novković 			break;
294*dcb10e3aSBojan Novković 	}
295*dcb10e3aSBojan Novković 	if ((val & clk_sc->fc_mask) != 0) {
296*dcb10e3aSBojan Novković 		/* The FC bit is still set, something went wrong. */
297*dcb10e3aSBojan Novković 		return (ENXIO);
298*dcb10e3aSBojan Novković 	}
299*dcb10e3aSBojan Novković 
300*dcb10e3aSBojan Novković 	return (0);
301*dcb10e3aSBojan Novković }
302*dcb10e3aSBojan Novković 
303*dcb10e3aSBojan Novković static int
k1_clk_set_freq(struct clknode * clk,uint64_t fparent,uint64_t * fout,int flags,int * stop)304*dcb10e3aSBojan Novković k1_clk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
305*dcb10e3aSBojan Novković     int flags, int *stop)
306*dcb10e3aSBojan Novković {
307*dcb10e3aSBojan Novković 	uint32_t div_mask, mux_mask;
308*dcb10e3aSBojan Novković 	int best_div, best_parent;
309*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
310*dcb10e3aSBojan Novković 	struct k1_clk_def *clk_sc;
311*dcb10e3aSBojan Novković 	uint64_t best_match;
312*dcb10e3aSBojan Novković 	uint32_t val;
313*dcb10e3aSBojan Novković 	device_t dev;
314*dcb10e3aSBojan Novković 	int p_idx;
315*dcb10e3aSBojan Novković 
316*dcb10e3aSBojan Novković 	dev = clknode_get_device(clk);
317*dcb10e3aSBojan Novković 	sc = device_get_softc(clknode_get_device(clk));
318*dcb10e3aSBojan Novković 	clk_sc = clknode_get_softc(clk);
319*dcb10e3aSBojan Novković 	p_idx = clknode_get_parent_idx(clk);
320*dcb10e3aSBojan Novković 
321*dcb10e3aSBojan Novković 	best_match = k1_clk_find_best_rate(clk, clk_sc, *fout, flags,
322*dcb10e3aSBojan Novković 	    &best_parent, &best_div);
323*dcb10e3aSBojan Novković 	*stop = 1;
324*dcb10e3aSBojan Novković 	if (best_match == 0 ||
325*dcb10e3aSBojan Novković 	    (best_match != *fout &&
326*dcb10e3aSBojan Novković 		CLK_SET_ROUND(flags) == CLK_SET_ROUND_EXACT))
327*dcb10e3aSBojan Novković 		return (ERANGE);
328*dcb10e3aSBojan Novković 
329*dcb10e3aSBojan Novković 	*fout = best_match;
330*dcb10e3aSBojan Novković 	if ((flags & CLK_SET_DRYRUN) != 0)
331*dcb10e3aSBojan Novković 		return (0);
332*dcb10e3aSBojan Novković 
333*dcb10e3aSBojan Novković 	DEVICE_LOCK(clk);
334*dcb10e3aSBojan Novković 	if (clk_sc->mux_nbits != 0 && p_idx != best_parent) {
335*dcb10e3aSBojan Novković 
336*dcb10e3aSBojan Novković 		clknode_set_parent_by_idx(clk, best_parent);
337*dcb10e3aSBojan Novković 		mux_mask = (1 << clk_sc->mux_nbits) - 1;
338*dcb10e3aSBojan Novković 		mux_mask <<= clk_sc->mux_shift;
339*dcb10e3aSBojan Novković 		val = READ4(sc, clk_sc->reg);
340*dcb10e3aSBojan Novković 		val &= ~mux_mask;
341*dcb10e3aSBojan Novković 		val |= (p_idx << clk_sc->mux_shift);
342*dcb10e3aSBojan Novković 		WRITE4(sc, clk_sc->reg, val);
343*dcb10e3aSBojan Novković 
344*dcb10e3aSBojan Novković 		if (k1_clk_send_fc_req(clk) != 0) {
345*dcb10e3aSBojan Novković 			device_printf(dev,
346*dcb10e3aSBojan Novković 			    "%s: failed mux frequency change request for '%s'\n",
347*dcb10e3aSBojan Novković 			    __func__, clk_sc->clkdef.name);
348*dcb10e3aSBojan Novković 			DEVICE_UNLOCK(clk);
349*dcb10e3aSBojan Novković 			return (ENXIO);
350*dcb10e3aSBojan Novković 		}
351*dcb10e3aSBojan Novković 	}
352*dcb10e3aSBojan Novković 
353*dcb10e3aSBojan Novković 	if (clk_sc->div_nbits != 0) {
354*dcb10e3aSBojan Novković 		div_mask = (1 << clk_sc->div_nbits) - 1;
355*dcb10e3aSBojan Novković 		val = READ4(sc, clk_sc->reg);
356*dcb10e3aSBojan Novković 		val &= ~(div_mask << clk_sc->div_shift);
357*dcb10e3aSBojan Novković 
358*dcb10e3aSBojan Novković 		/* The final divisor value is calculated as 'div_value' + 1. */
359*dcb10e3aSBojan Novković 		val |= (best_div - 1) << clk_sc->div_shift;
360*dcb10e3aSBojan Novković 		WRITE4(sc, clk_sc->reg, val);
361*dcb10e3aSBojan Novković 
362*dcb10e3aSBojan Novković 		if (k1_clk_send_fc_req(clk) != 0) {
363*dcb10e3aSBojan Novković 			device_printf(dev,
364*dcb10e3aSBojan Novković 			    "%s: failed div frequency change request for '%s'\n",
365*dcb10e3aSBojan Novković 			    __func__, clk_sc->clkdef.name);
366*dcb10e3aSBojan Novković 			DEVICE_UNLOCK(clk);
367*dcb10e3aSBojan Novković 			return (ENXIO);
368*dcb10e3aSBojan Novković 		}
369*dcb10e3aSBojan Novković 	}
370*dcb10e3aSBojan Novković 	DEVICE_UNLOCK(clk);
371*dcb10e3aSBojan Novković 
372*dcb10e3aSBojan Novković 	return (0);
373*dcb10e3aSBojan Novković }
374*dcb10e3aSBojan Novković 
375*dcb10e3aSBojan Novković static int
k1_clk_init(struct clknode * clk,device_t dev)376*dcb10e3aSBojan Novković k1_clk_init(struct clknode *clk, device_t dev)
377*dcb10e3aSBojan Novković {
378*dcb10e3aSBojan Novković 	struct k1_clkdev_softc *sc;
379*dcb10e3aSBojan Novković 	struct k1_clk_def *clk_sc;
380*dcb10e3aSBojan Novković 	uint32_t val, mux_mask;
381*dcb10e3aSBojan Novković 	int pidx;
382*dcb10e3aSBojan Novković 
383*dcb10e3aSBojan Novković 	sc = device_get_softc(clknode_get_device(clk));
384*dcb10e3aSBojan Novković 	clk_sc = clknode_get_softc(clk);
385*dcb10e3aSBojan Novković 
386*dcb10e3aSBojan Novković 	if (clk_sc->mux_shift == 0) {
387*dcb10e3aSBojan Novković 		clknode_init_parent_idx(clk, 0);
388*dcb10e3aSBojan Novković 		return (0);
389*dcb10e3aSBojan Novković 	}
390*dcb10e3aSBojan Novković 	DEVICE_LOCK(clk);
391*dcb10e3aSBojan Novković 	val = READ4(sc, clk_sc->reg);
392*dcb10e3aSBojan Novković 	DEVICE_UNLOCK(clk);
393*dcb10e3aSBojan Novković 	mux_mask = (1 << clk_sc->mux_nbits) - 1;
394*dcb10e3aSBojan Novković 	pidx = (val >> clk_sc->mux_shift) & mux_mask;
395*dcb10e3aSBojan Novković 
396*dcb10e3aSBojan Novković 	clknode_init_parent_idx(clk, pidx);
397*dcb10e3aSBojan Novković 
398*dcb10e3aSBojan Novković 	return (0);
399*dcb10e3aSBojan Novković }
400*dcb10e3aSBojan Novković 
401*dcb10e3aSBojan Novković static clknode_method_t k1_clknode_methods[] = {
402*dcb10e3aSBojan Novković 	CLKNODEMETHOD(clknode_init,		k1_clk_init),
403*dcb10e3aSBojan Novković 	CLKNODEMETHOD(clknode_set_gate,		k1_clk_set_gate),
404*dcb10e3aSBojan Novković 	CLKNODEMETHOD(clknode_set_freq,		k1_clk_set_freq),
405*dcb10e3aSBojan Novković 	CLKNODEMETHOD_END
406*dcb10e3aSBojan Novković };
407*dcb10e3aSBojan Novković 
408*dcb10e3aSBojan Novković DEFINE_CLASS_1(k1_clknode, k1_clknode_class,
409*dcb10e3aSBojan Novković     k1_clknode_methods, sizeof(struct k1_clk_def), clknode_class);
410