Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/clk/starfive/jh7110_clk_pll.c
39537 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2024 Jari Sihvola <[email protected]>
5
* Copyright (c) 2024 The FreeBSD Foundation
6
*
7
* Portions of this software were developed by Mitchell Horne
8
* <[email protected]> under sponsorship from the FreeBSD Foundation.
9
*/
10
11
#include <sys/param.h>
12
#include <sys/systm.h>
13
#include <sys/bus.h>
14
#include <sys/kernel.h>
15
#include <sys/module.h>
16
#include <sys/mutex.h>
17
18
#include <machine/bus.h>
19
20
#include <dev/fdt/simplebus.h>
21
#include <dev/ofw/ofw_bus.h>
22
#include <dev/ofw/ofw_bus_subr.h>
23
24
#include <dev/clk/clk.h>
25
#include <dev/clk/starfive/jh7110_clk.h>
26
#include <dev/clk/starfive/jh7110_clk_pll.h>
27
#include <dev/syscon/syscon.h>
28
29
#include <dt-bindings/clock/starfive,jh7110-crg.h>
30
31
#include "clkdev_if.h"
32
#include "syscon_if.h"
33
34
#define JH7110_SYS_SYSCON_SYSCFG24 0x18
35
#define JH7110_SYS_SYSCON_SYSCFG28 0x1c
36
#define JH7110_SYS_SYSCON_SYSCFG32 0x20
37
#define JH7110_SYS_SYSCON_SYSCFG36 0x24
38
#define JH7110_SYS_SYSCON_SYSCFG40 0x28
39
#define JH7110_SYS_SYSCON_SYSCFG44 0x2c
40
#define JH7110_SYS_SYSCON_SYSCFG48 0x30
41
#define JH7110_SYS_SYSCON_SYSCFG52 0x34
42
43
#define DEVICE_LOCK(_clk) \
44
CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
45
#define DEVICE_UNLOCK(_clk) \
46
CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
47
48
#define PLL_MASK_FILL(sc, id) \
49
do { \
50
sc->dacpd_mask = PLL## id ##_DACPD_MASK; \
51
sc->dsmpd_mask = PLL## id ##_DSMPD_MASK; \
52
sc->fbdiv_mask = PLL## id ##_FBDIV_MASK; \
53
sc->frac_mask = PLL## id ##_FRAC_MASK; \
54
sc->prediv_mask = PLL## id ##_PREDIV_MASK; \
55
sc->postdiv1_mask = PLL## id ##_POSTDIV1_MASK; \
56
} while (0)
57
58
#define PLL_SHIFT_FILL(sc, id) \
59
do { \
60
sc->dacpd_shift = PLL## id ##_DACPD_SHIFT; \
61
sc->dsmpd_shift = PLL## id ##_DSMPD_SHIFT; \
62
sc->fbdiv_shift = PLL## id ##_FBDIV_SHIFT; \
63
sc->frac_shift = PLL## id ##_FRAC_SHIFT; \
64
sc->prediv_shift = PLL## id ##_PREDIV_SHIFT; \
65
sc->postdiv1_shift = PLL## id ##_POSTDIV1_SHIFT; \
66
} while (0)
67
68
struct jh7110_clk_pll_softc {
69
struct mtx mtx;
70
struct clkdom *clkdom;
71
struct syscon *syscon;
72
};
73
74
struct jh7110_pll_clknode_softc {
75
uint32_t dacpd_offset;
76
uint32_t dsmpd_offset;
77
uint32_t fbdiv_offset;
78
uint32_t frac_offset;
79
uint32_t prediv_offset;
80
uint32_t postdiv1_offset;
81
82
uint32_t dacpd_mask;
83
uint32_t dsmpd_mask;
84
uint32_t fbdiv_mask;
85
uint32_t frac_mask;
86
uint32_t prediv_mask;
87
uint32_t postdiv1_mask;
88
89
uint32_t dacpd_shift;
90
uint32_t dsmpd_shift;
91
uint32_t fbdiv_shift;
92
uint32_t frac_shift;
93
uint32_t prediv_shift;
94
uint32_t postdiv1_shift;
95
96
const struct jh7110_pll_syscon_value *syscon_arr;
97
int syscon_nitems;
98
};
99
100
static const char *pll_parents[] = { "osc" };
101
102
static struct jh7110_clk_def pll_out_clks[] = {
103
{
104
.clkdef.id = JH7110_PLLCLK_PLL0_OUT,
105
.clkdef.name = "pll0_out",
106
.clkdef.parent_names = pll_parents,
107
.clkdef.parent_cnt = nitems(pll_parents),
108
.clkdef.flags = CLK_NODE_STATIC_STRINGS,
109
},
110
{
111
.clkdef.id = JH7110_PLLCLK_PLL1_OUT,
112
.clkdef.name = "pll1_out",
113
.clkdef.parent_names = pll_parents,
114
.clkdef.parent_cnt = nitems(pll_parents),
115
.clkdef.flags = CLK_NODE_STATIC_STRINGS,
116
},
117
{
118
.clkdef.id = JH7110_PLLCLK_PLL2_OUT,
119
.clkdef.name = "pll2_out",
120
.clkdef.parent_names = pll_parents,
121
.clkdef.parent_cnt = nitems(pll_parents),
122
.clkdef.flags = CLK_NODE_STATIC_STRINGS,
123
},
124
};
125
126
static int jh7110_clk_pll_register(struct clkdom *clkdom,
127
struct jh7110_clk_def *clkdef);
128
129
static int
130
jh7110_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
131
{
132
struct jh7110_clk_pll_softc *sc;
133
struct jh7110_pll_clknode_softc *clk_sc;
134
uint32_t dacpd, dsmpd, fbdiv, prediv, postdiv1;
135
uint64_t frac, fcal = 0;
136
137
sc = device_get_softc(clknode_get_device(clk));
138
clk_sc = clknode_get_softc(clk);
139
140
DEVICE_LOCK(clk);
141
142
dacpd = (SYSCON_READ_4(sc->syscon, clk_sc->dacpd_offset) & clk_sc->dacpd_mask) >>
143
clk_sc->dacpd_shift;
144
dsmpd = (SYSCON_READ_4(sc->syscon, clk_sc->dsmpd_offset) & clk_sc->dsmpd_mask) >>
145
clk_sc->dsmpd_shift;
146
fbdiv = (SYSCON_READ_4(sc->syscon, clk_sc->fbdiv_offset) & clk_sc->fbdiv_mask) >>
147
clk_sc->fbdiv_shift;
148
prediv = (SYSCON_READ_4(sc->syscon, clk_sc->prediv_offset) & clk_sc->prediv_mask) >>
149
clk_sc->prediv_shift;
150
postdiv1 = (SYSCON_READ_4(sc->syscon, clk_sc->postdiv1_offset) &
151
clk_sc->postdiv1_mask) >> clk_sc->postdiv1_shift;
152
frac = (SYSCON_READ_4(sc->syscon, clk_sc->frac_offset) & clk_sc->frac_mask) >>
153
clk_sc->frac_shift;
154
155
DEVICE_UNLOCK(clk);
156
157
/* dacpd and dsmpd both being 0 entails Fraction Multiple Mode */
158
if (dacpd == 0 && dsmpd == 0)
159
fcal = frac * FRAC_PATR_SIZE / (1 << 24);
160
161
*freq = *freq / FRAC_PATR_SIZE * (fbdiv * FRAC_PATR_SIZE + fcal) /
162
prediv / (1 << postdiv1);
163
164
return (0);
165
}
166
167
static int
168
jh7110_clk_pll_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
169
int flags, int *done)
170
{
171
struct jh7110_clk_pll_softc *sc;
172
struct jh7110_pll_clknode_softc *clk_sc;
173
const struct jh7110_pll_syscon_value *syscon_val = NULL;
174
175
sc = device_get_softc(clknode_get_device(clk));
176
clk_sc = clknode_get_softc(clk);
177
178
for (int i = 0; i != clk_sc->syscon_nitems; i++) {
179
if (*fout == clk_sc->syscon_arr[i].freq) {
180
syscon_val = &clk_sc->syscon_arr[i];
181
}
182
}
183
184
if (syscon_val == NULL) {
185
printf("%s: tried to set an unknown frequency %ju for %s\n",
186
__func__, *fout, clknode_get_name(clk));
187
return (EINVAL);
188
}
189
190
if ((flags & CLK_SET_DRYRUN) != 0) {
191
*done = 1;
192
return (0);
193
}
194
195
DEVICE_LOCK(clk);
196
197
SYSCON_MODIFY_4(sc->syscon, clk_sc->dacpd_offset, clk_sc->dacpd_mask,
198
syscon_val->dacpd << clk_sc->dacpd_shift & clk_sc->dacpd_mask);
199
SYSCON_MODIFY_4(sc->syscon, clk_sc->dsmpd_offset, clk_sc->dsmpd_mask,
200
syscon_val->dsmpd << clk_sc->dsmpd_shift & clk_sc->dsmpd_mask);
201
SYSCON_MODIFY_4(sc->syscon, clk_sc->prediv_offset, clk_sc->prediv_mask,
202
syscon_val->prediv << clk_sc->prediv_shift & clk_sc->prediv_mask);
203
SYSCON_MODIFY_4(sc->syscon, clk_sc->fbdiv_offset, clk_sc->fbdiv_mask,
204
syscon_val->fbdiv << clk_sc->fbdiv_shift & clk_sc->fbdiv_mask);
205
SYSCON_MODIFY_4(sc->syscon, clk_sc->postdiv1_offset,
206
clk_sc->postdiv1_mask, (syscon_val->postdiv1 >> 1) <<
207
clk_sc->postdiv1_shift & clk_sc->postdiv1_mask);
208
209
if (!syscon_val->dacpd && !syscon_val->dsmpd) {
210
SYSCON_MODIFY_4(sc->syscon, clk_sc->frac_offset, clk_sc->frac_mask,
211
syscon_val->frac << clk_sc->frac_shift & clk_sc->frac_mask);
212
}
213
214
DEVICE_UNLOCK(clk);
215
216
*done = 1;
217
return (0);
218
}
219
220
static int
221
jh7110_clk_pll_init(struct clknode *clk, device_t dev)
222
{
223
clknode_init_parent_idx(clk, 0);
224
225
return (0);
226
}
227
228
static int
229
jh7110_clk_pll_probe(device_t dev)
230
{
231
if (!ofw_bus_status_okay(dev))
232
return (ENXIO);
233
234
if (!ofw_bus_is_compatible(dev, "starfive,jh7110-pll"))
235
return (ENXIO);
236
237
device_set_desc(dev, "StarFive JH7110 PLL clock generator");
238
239
return (BUS_PROBE_DEFAULT);
240
}
241
242
static int
243
jh7110_clk_pll_attach(device_t dev)
244
{
245
struct jh7110_clk_pll_softc *sc;
246
int error;
247
248
sc = device_get_softc(dev);
249
250
mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
251
252
sc->clkdom = clkdom_create(dev);
253
if (sc->clkdom == NULL) {
254
device_printf(dev, "Couldn't create clkdom\n");
255
return (ENXIO);
256
}
257
258
error = syscon_get_by_ofw_node(dev, OF_parent(ofw_bus_get_node(dev)),
259
&sc->syscon);
260
if (error != 0) {
261
device_printf(dev, "Couldn't get syscon handle of parent\n");
262
return (error);
263
}
264
265
for (int i = 0; i < nitems(pll_out_clks); i++) {
266
error = jh7110_clk_pll_register(sc->clkdom, &pll_out_clks[i]);
267
if (error != 0)
268
device_printf(dev, "Couldn't register clock %s: %d\n",
269
pll_out_clks[i].clkdef.name, error);
270
}
271
272
error = clkdom_finit(sc->clkdom);
273
if (error != 0) {
274
device_printf(dev, "clkdom_finit() returned %d\n", error);
275
}
276
277
if (bootverbose)
278
clkdom_dump(sc->clkdom);
279
280
return (0);
281
}
282
283
static void
284
jh7110_clk_pll_device_lock(device_t dev)
285
{
286
struct jh7110_clk_pll_softc *sc;
287
288
sc = device_get_softc(dev);
289
mtx_lock(&sc->mtx);
290
}
291
292
static void
293
jh7110_clk_pll_device_unlock(device_t dev)
294
{
295
struct jh7110_clk_pll_softc *sc;
296
297
sc = device_get_softc(dev);
298
mtx_unlock(&sc->mtx);
299
}
300
301
static clknode_method_t jh7110_pllnode_methods[] = {
302
/* Device interface */
303
CLKNODEMETHOD(clknode_init, jh7110_clk_pll_init),
304
CLKNODEMETHOD(clknode_recalc_freq, jh7110_clk_pll_recalc_freq),
305
CLKNODEMETHOD(clknode_set_freq, jh7110_clk_pll_set_freq),
306
307
CLKNODEMETHOD_END
308
};
309
310
static device_method_t jh7110_clk_pll_methods[] = {
311
/* Device interface */
312
DEVMETHOD(device_probe, jh7110_clk_pll_probe),
313
DEVMETHOD(device_attach, jh7110_clk_pll_attach),
314
315
/* clkdev interface */
316
DEVMETHOD(clkdev_device_lock, jh7110_clk_pll_device_lock),
317
DEVMETHOD(clkdev_device_unlock, jh7110_clk_pll_device_unlock),
318
319
DEVMETHOD_END
320
};
321
322
DEFINE_CLASS_1(jh7110_pllnode, jh7110_pllnode_class, jh7110_pllnode_methods,
323
sizeof(struct jh7110_pll_clknode_softc), clknode_class);
324
DEFINE_CLASS_0(jh7110_clk_pll, jh7110_clk_pll_driver, jh7110_clk_pll_methods,
325
sizeof(struct jh7110_clk_pll_softc));
326
EARLY_DRIVER_MODULE(jh7110_clk_pll, simplebus, jh7110_clk_pll_driver, 0, 0,
327
BUS_PASS_BUS + BUS_PASS_ORDER_EARLY);
328
MODULE_VERSION(jh7110_clk_pll, 1);
329
330
int
331
jh7110_clk_pll_register(struct clkdom *clkdom, struct jh7110_clk_def *clkdef)
332
{
333
struct clknode *clk = NULL;
334
struct jh7110_pll_clknode_softc *sc;
335
336
clk = clknode_create(clkdom, &jh7110_pllnode_class, &clkdef->clkdef);
337
if (clk == NULL)
338
return (1);
339
340
sc = clknode_get_softc(clk);
341
342
switch (clkdef->clkdef.id) {
343
case JH7110_PLLCLK_PLL0_OUT:
344
sc->syscon_arr = jh7110_pll0_syscon_freq;
345
sc->syscon_nitems = nitems(jh7110_pll0_syscon_freq);
346
PLL_MASK_FILL(sc, 0);
347
PLL_SHIFT_FILL(sc, 0);
348
sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG24;
349
sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG24;
350
sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG28;
351
sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG32;
352
sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG36;
353
sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG32;
354
break;
355
case JH7110_PLLCLK_PLL1_OUT:
356
sc->syscon_arr = jh7110_pll1_syscon_freq;
357
sc->syscon_nitems = nitems(jh7110_pll1_syscon_freq);
358
PLL_MASK_FILL(sc, 1);
359
PLL_SHIFT_FILL(sc, 1);
360
sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG36;
361
sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG36;
362
sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG36;
363
sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG40;
364
sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG44;
365
sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG40;
366
break;
367
case JH7110_PLLCLK_PLL2_OUT:
368
sc->syscon_arr = jh7110_pll2_syscon_freq;
369
sc->syscon_nitems = nitems(jh7110_pll2_syscon_freq);
370
PLL_MASK_FILL(sc, 2);
371
PLL_SHIFT_FILL(sc, 2);
372
sc->dacpd_offset = JH7110_SYS_SYSCON_SYSCFG44;
373
sc->dsmpd_offset = JH7110_SYS_SYSCON_SYSCFG44;
374
sc->fbdiv_offset = JH7110_SYS_SYSCON_SYSCFG44;
375
sc->frac_offset = JH7110_SYS_SYSCON_SYSCFG48;
376
sc->prediv_offset = JH7110_SYS_SYSCON_SYSCFG52;
377
sc->postdiv1_offset = JH7110_SYS_SYSCON_SYSCFG48;
378
break;
379
default:
380
return (EINVAL);
381
}
382
383
clknode_register(clkdom, clk);
384
385
return (0);
386
}
387
388