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