Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/clk/xilinx/zynqmp_clock.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/cdefs.h>
29
30
#include <sys/param.h>
31
#include <sys/systm.h>
32
#include <sys/kernel.h>
33
#include <sys/module.h>
34
#include <sys/malloc.h>
35
#include <sys/bus.h>
36
#include <sys/cpu.h>
37
#include <machine/bus.h>
38
#include <sys/queue.h>
39
40
#include <dev/ofw/openfirm.h>
41
#include <dev/ofw/ofw_bus.h>
42
#include <dev/ofw/ofw_bus_subr.h>
43
44
#include <dev/clk/clk.h>
45
#include <dev/clk/clk_fixed.h>
46
47
#include <dev/clk/xilinx/zynqmp_clk_mux.h>
48
#include <dev/clk/xilinx/zynqmp_clk_pll.h>
49
#include <dev/clk/xilinx/zynqmp_clk_fixed.h>
50
#include <dev/clk/xilinx/zynqmp_clk_div.h>
51
#include <dev/clk/xilinx/zynqmp_clk_gate.h>
52
53
#include <dev/firmware/xilinx/pm_defs.h>
54
55
#include "clkdev_if.h"
56
#include "zynqmp_firmware_if.h"
57
58
#define ZYNQMP_MAX_NAME_LEN 16
59
#define ZYNQMP_MAX_NODES 6
60
#define ZYNQMP_MAX_PARENTS 100
61
62
#define ZYNQMP_CLK_IS_VALID (1 << 0)
63
#define ZYNQMP_CLK_IS_EXT (1 << 2)
64
65
#define ZYNQMP_GET_NODE_TYPE(x) (x & 0x7)
66
#define ZYNQMP_GET_NODE_CLKFLAGS(x) ((x >> 8) & 0xFF)
67
#define ZYNQMP_GET_NODE_TYPEFLAGS(x) ((x >> 24) & 0xF)
68
69
enum ZYNQMP_NODE_TYPE {
70
CLK_NODE_TYPE_NULL = 0,
71
CLK_NODE_TYPE_MUX,
72
CLK_NODE_TYPE_PLL,
73
CLK_NODE_TYPE_FIXED,
74
CLK_NODE_TYPE_DIV0,
75
CLK_NODE_TYPE_DIV1,
76
CLK_NODE_TYPE_GATE,
77
};
78
79
/*
80
* Clock IDs in the firmware starts at 0 but
81
* exported clocks (and so clock exposed by the clock framework)
82
* starts at 1
83
*/
84
#define ZYNQMP_ID_TO_CLK(x) ((x) + 1)
85
#define CLK_ID_TO_ZYNQMP(x) ((x) - 1)
86
87
struct zynqmp_clk {
88
TAILQ_ENTRY(zynqmp_clk) next;
89
struct clknode_init_def clkdef;
90
uint32_t id;
91
uint32_t parentids[ZYNQMP_MAX_PARENTS];
92
uint32_t topology[ZYNQMP_MAX_NODES];
93
uint32_t attributes;
94
};
95
96
struct zynqmp_clock_softc {
97
device_t dev;
98
device_t parent;
99
phandle_t node;
100
clk_t clk_pss_ref;
101
clk_t clk_video;
102
clk_t clk_pss_alt_ref;
103
clk_t clk_aux_ref;
104
clk_t clk_gt_crx_ref;
105
struct clkdom *clkdom;
106
};
107
108
struct name_resp {
109
char name[16];
110
};
111
112
struct zynqmp_clk_softc {
113
struct zynqmp_clk *clk;
114
device_t firmware;
115
uint32_t id;
116
};
117
118
static int
119
zynqmp_clk_init(struct clknode *clk, device_t dev)
120
{
121
122
clknode_init_parent_idx(clk, 0);
123
return (0);
124
}
125
126
static clknode_method_t zynqmp_clk_clknode_methods[] = {
127
/* Device interface */
128
CLKNODEMETHOD(clknode_init, zynqmp_clk_init),
129
CLKNODEMETHOD_END
130
};
131
132
DEFINE_CLASS_1(zynqmp_clk_clknode, zynqmp_clk_clknode_class,
133
zynqmp_clk_clknode_methods, sizeof(struct zynqmp_clk_softc), clknode_class);
134
135
static int
136
zynqmp_clk_register(struct clkdom *clkdom, device_t fw, struct zynqmp_clk *clkdef)
137
{
138
struct clknode *clknode;
139
struct zynqmp_clk_softc *sc;
140
char *prev_clock_name = NULL;
141
char *clkname, *parent_name;
142
struct clknode_init_def *zynqclk;
143
int i;
144
145
for (i = 0; i < ZYNQMP_MAX_NODES; i++) {
146
/* Bail early if we have no node */
147
if (ZYNQMP_GET_NODE_TYPE(clkdef->topology[i]) == CLK_NODE_TYPE_NULL)
148
break;
149
zynqclk = malloc(sizeof(*zynqclk), M_DEVBUF, M_WAITOK | M_ZERO);
150
zynqclk->id = clkdef->clkdef.id;
151
/* For the first node in the topology we use the main clock parents */
152
if (i == 0) {
153
zynqclk->parent_cnt = clkdef->clkdef.parent_cnt;
154
zynqclk->parent_names = clkdef->clkdef.parent_names;
155
} else {
156
zynqclk->parent_cnt = 1;
157
zynqclk->parent_names = malloc(sizeof(char *) * zynqclk->parent_cnt, M_DEVBUF, M_ZERO | M_WAITOK);
158
parent_name = strdup(prev_clock_name, M_DEVBUF);
159
zynqclk->parent_names[0] = (const char *)parent_name;
160
}
161
/* Register the clock node based on the topology type */
162
switch (ZYNQMP_GET_NODE_TYPE(clkdef->topology[i])) {
163
case CLK_NODE_TYPE_MUX:
164
asprintf(&clkname, M_DEVBUF, "%s_mux", clkdef->clkdef.name);
165
zynqclk->name = (const char *)clkname;
166
zynqmp_clk_mux_register(clkdom, fw, zynqclk);
167
break;
168
case CLK_NODE_TYPE_PLL:
169
asprintf(&clkname, M_DEVBUF, "%s_pll", clkdef->clkdef.name);
170
zynqclk->name = (const char *)clkname;
171
zynqmp_clk_pll_register(clkdom, fw, zynqclk);
172
break;
173
case CLK_NODE_TYPE_FIXED:
174
asprintf(&clkname, M_DEVBUF, "%s_fixed", clkdef->clkdef.name);
175
zynqclk->name = (const char *)clkname;
176
zynqmp_clk_fixed_register(clkdom, fw, zynqclk);
177
break;
178
case CLK_NODE_TYPE_DIV0:
179
asprintf(&clkname, M_DEVBUF, "%s_div0", clkdef->clkdef.name);
180
zynqclk->name = (const char *)clkname;
181
zynqmp_clk_div_register(clkdom, fw, zynqclk, CLK_DIV_TYPE_DIV0);
182
break;
183
case CLK_NODE_TYPE_DIV1:
184
asprintf(&clkname, M_DEVBUF, "%s_div1", clkdef->clkdef.name);
185
zynqclk->name = (const char *)clkname;
186
zynqmp_clk_div_register(clkdom, fw, zynqclk, CLK_DIV_TYPE_DIV1);
187
break;
188
case CLK_NODE_TYPE_GATE:
189
asprintf(&clkname, M_DEVBUF, "%s_gate", clkdef->clkdef.name);
190
zynqclk->name = (const char *)clkname;
191
zynqmp_clk_gate_register(clkdom, fw, zynqclk);
192
break;
193
case CLK_NODE_TYPE_NULL:
194
default:
195
clkname = NULL;
196
break;
197
}
198
if (i != 0) {
199
free(parent_name, M_DEVBUF);
200
free(zynqclk->parent_names, M_DEVBUF);
201
}
202
if (clkname != NULL)
203
prev_clock_name = strdup(clkname, M_DEVBUF);
204
free(clkname, M_DEVBUF);
205
free(zynqclk, M_DEVBUF);
206
}
207
208
/* Register main clock */
209
clkdef->clkdef.name = clkdef->clkdef.name;
210
clkdef->clkdef.parent_cnt = 1;
211
clkdef->clkdef.parent_names = malloc(sizeof(char *) * clkdef->clkdef.parent_cnt, M_DEVBUF, M_ZERO | M_WAITOK);
212
clkdef->clkdef.parent_names[0] = strdup(prev_clock_name, M_DEVBUF);
213
clknode = clknode_create(clkdom, &zynqmp_clk_clknode_class, &clkdef->clkdef);
214
if (clknode == NULL)
215
return (1);
216
sc = clknode_get_softc(clknode);
217
sc->id = clkdef->clkdef.id - 1;
218
sc->firmware = fw;
219
sc->clk = clkdef;
220
clknode_register(clkdom, clknode);
221
return (0);
222
}
223
224
static int
225
zynqmp_fw_clk_get_name(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id)
226
{
227
char *clkname;
228
uint32_t query_data[4];
229
int rv;
230
231
rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_NAME, id, 0, 0, query_data);
232
if (rv != 0)
233
return (rv);
234
if (query_data[0] == '\0')
235
return (EINVAL);
236
clkname = malloc(ZYNQMP_MAX_NAME_LEN, M_DEVBUF, M_ZERO | M_WAITOK);
237
memcpy(clkname, query_data, ZYNQMP_MAX_NAME_LEN);
238
clk->clkdef.name = clkname;
239
return (0);
240
}
241
242
static int
243
zynqmp_fw_clk_get_attributes(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id)
244
{
245
uint32_t query_data[4];
246
int rv;
247
248
rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_ATTRIBUTES, id, 0, 0, query_data);
249
if (rv != 0)
250
return (rv);
251
clk->attributes = query_data[1];
252
return (0);
253
}
254
255
static int
256
zynqmp_fw_clk_get_parents(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id)
257
{
258
int rv, i;
259
uint32_t query_data[4];
260
261
for (i = 0; i < ZYNQMP_MAX_PARENTS; i += 3) {
262
clk->parentids[i] = -1;
263
clk->parentids[i + 1] = -1;
264
clk->parentids[i + 2] = -1;
265
rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_PARENTS, id, i, 0, query_data);
266
clk->parentids[i] = query_data[1] & 0xFFFF;
267
clk->parentids[i + 1] = query_data[2] & 0xFFFF;
268
clk->parentids[i + 2] = query_data[3] & 0xFFFF;
269
if ((int32_t)query_data[1] == -1) {
270
clk->parentids[i] = -1;
271
break;
272
}
273
clk->parentids[i] += 1;
274
clk->clkdef.parent_cnt++;
275
if ((int32_t)query_data[2] == -1) {
276
clk->parentids[i + 1] = -1;
277
break;
278
}
279
clk->parentids[i + 1] += 1;
280
clk->clkdef.parent_cnt++;
281
if ((int32_t)query_data[3] == -1) {
282
clk->parentids[i + 2] = -1;
283
break;
284
}
285
clk->parentids[i + 2] += 1;
286
clk->clkdef.parent_cnt++;
287
if ((int32_t)query_data[1] == -2)
288
clk->parentids[i] = -2;
289
if ((int32_t)query_data[2] == -2)
290
clk->parentids[i + 1] = -2;
291
if ((int32_t)query_data[3] == -2)
292
clk->parentids[i + 2] = -2;
293
if (rv != 0)
294
break;
295
}
296
return (0);
297
}
298
299
static int
300
zynqmp_fw_clk_get_topology(struct zynqmp_clock_softc *sc, struct zynqmp_clk *clk, uint32_t id)
301
{
302
uint32_t query_data[4];
303
int rv;
304
305
rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_TOPOLOGY, id, 0, 0, query_data);
306
if (rv != 0)
307
return (rv);
308
clk->topology[0] = query_data[1];
309
clk->topology[1] = query_data[2];
310
clk->topology[2] = query_data[3];
311
if (query_data[3] == '\0')
312
goto out;
313
rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent, PM_QID_CLOCK_GET_TOPOLOGY, id, 3, 0, query_data);
314
if (rv != 0)
315
return (rv);
316
clk->topology[3] = query_data[1];
317
clk->topology[4] = query_data[2];
318
clk->topology[5] = query_data[3];
319
320
out:
321
return (0);
322
}
323
324
static int
325
zynqmp_clock_ofw_map(struct clkdom *clkdom, uint32_t ncells,
326
phandle_t *cells, struct clknode **clk)
327
{
328
329
if (ncells != 1)
330
return (ERANGE);
331
*clk = clknode_find_by_id(clkdom, ZYNQMP_ID_TO_CLK(cells[0]));
332
if (*clk == NULL)
333
return (ENXIO);
334
return (0);
335
}
336
337
static int
338
zynqmp_fw_clk_get_all(struct zynqmp_clock_softc *sc)
339
{
340
TAILQ_HEAD(tailhead, zynqmp_clk) clk_list;
341
struct zynqmp_clk *clk, *tmp, *tmp2;
342
char *clkname;
343
int rv, i;
344
uint32_t query_data[4], num_clock;
345
346
TAILQ_INIT(&clk_list);
347
rv = ZYNQMP_FIRMWARE_QUERY_DATA(sc->parent,
348
PM_QID_CLOCK_GET_NUM_CLOCKS,
349
0,
350
0,
351
0,
352
query_data);
353
if (rv != 0) {
354
device_printf(sc->dev, "Cannot get clock details from the firmware\n");
355
return (ENXIO);
356
}
357
358
num_clock = query_data[1];
359
for (i = 0; i < num_clock; i++) {
360
clk = malloc(sizeof(*clk), M_DEVBUF, M_WAITOK | M_ZERO);
361
clk->clkdef.id = ZYNQMP_ID_TO_CLK(i);
362
zynqmp_fw_clk_get_name(sc, clk, i);
363
zynqmp_fw_clk_get_attributes(sc, clk, i);
364
if ((clk->attributes & ZYNQMP_CLK_IS_VALID) == 0) {
365
free(clk, M_DEVBUF);
366
continue;
367
}
368
if (clk->attributes & ZYNQMP_CLK_IS_EXT)
369
goto skip_ext;
370
/* Get parents id */
371
rv = zynqmp_fw_clk_get_parents(sc, clk, i);
372
if (rv != 0) {
373
device_printf(sc->dev, "Cannot get parent for %s\n", clk->clkdef.name);
374
free(clk, M_DEVBUF);
375
continue;
376
}
377
/* Get topology */
378
rv = zynqmp_fw_clk_get_topology(sc, clk, i);
379
if (rv != 0) {
380
device_printf(sc->dev, "Cannot get topology for %s\n", clk->clkdef.name);
381
free(clk, M_DEVBUF);
382
continue;
383
}
384
skip_ext:
385
TAILQ_INSERT_TAIL(&clk_list, clk, next);
386
}
387
388
/* Add a dummy clock */
389
clk = malloc(sizeof(*clk), M_DEVBUF, M_WAITOK | M_ZERO);
390
clkname = strdup("dummy", M_DEVBUF);
391
clk->clkdef.name = (const char *)clkname;
392
clk->clkdef.id = i;
393
clk->attributes = ZYNQMP_CLK_IS_EXT;
394
TAILQ_INSERT_TAIL(&clk_list, clk, next);
395
396
/* Map parents id to name */
397
TAILQ_FOREACH_SAFE(clk, &clk_list, next, tmp) {
398
if (clk->attributes & ZYNQMP_CLK_IS_EXT)
399
continue;
400
clk->clkdef.parent_names = malloc(sizeof(char *) * clk->clkdef.parent_cnt, M_DEVBUF, M_ZERO | M_WAITOK);
401
for (i = 0; i < ZYNQMP_MAX_PARENTS; i++) {
402
if (clk->parentids[i] == -1)
403
break;
404
if (clk->parentids[i] == -2) {
405
clk->clkdef.parent_names[i] = strdup("dummy", M_DEVBUF);
406
continue;
407
}
408
TAILQ_FOREACH(tmp2, &clk_list, next) {
409
if (tmp2->clkdef.id == clk->parentids[i]) {
410
if (tmp2->attributes & ZYNQMP_CLK_IS_EXT) {
411
int idx;
412
413
if (ofw_bus_find_string_index( sc->node,
414
"clock-names", tmp2->clkdef.name, &idx) == ENOENT)
415
clk->clkdef.parent_names[i] = strdup("dummy", M_DEVBUF);
416
else
417
clk->clkdef.parent_names[i] = strdup(tmp2->clkdef.name, M_DEVBUF);
418
}
419
else
420
clk->clkdef.parent_names[i] = strdup(tmp2->clkdef.name, M_DEVBUF);
421
break;
422
}
423
}
424
}
425
}
426
427
sc->clkdom = clkdom_create(sc->dev);
428
if (sc->clkdom == NULL)
429
panic("Cannot create clkdom\n");
430
clkdom_set_ofw_mapper(sc->clkdom, zynqmp_clock_ofw_map);
431
432
/* Register the clocks */
433
TAILQ_FOREACH_SAFE(clk, &clk_list, next, tmp) {
434
if (clk->attributes & ZYNQMP_CLK_IS_EXT) {
435
if (strcmp(clk->clkdef.name, "dummy") == 0) {
436
struct clk_fixed_def dummy;
437
438
bzero(&dummy, sizeof(dummy));
439
dummy.clkdef.id = clk->clkdef.id;
440
dummy.clkdef.name = strdup("dummy", M_DEVBUF);
441
clknode_fixed_register(sc->clkdom, &dummy);
442
free(__DECONST(char *, dummy.clkdef.name), M_DEVBUF);
443
}
444
} else
445
zynqmp_clk_register(sc->clkdom, sc->parent, clk);
446
447
TAILQ_REMOVE(&clk_list, clk, next);
448
for (i = 0; i < clk->clkdef.parent_cnt; i++)
449
free(__DECONST(char *, clk->clkdef.parent_names[i]), M_DEVBUF);
450
free(clk->clkdef.parent_names, M_DEVBUF);
451
free(__DECONST(char *, clk->clkdef.name), M_DEVBUF);
452
free(clk, M_DEVBUF);
453
}
454
455
if (clkdom_finit(sc->clkdom) != 0)
456
panic("cannot finalize clkdom initialization\n");
457
458
if (bootverbose)
459
clkdom_dump(sc->clkdom);
460
461
return (0);
462
}
463
464
static int
465
zynqmp_clock_probe(device_t dev)
466
{
467
468
if (!ofw_bus_status_okay(dev))
469
return (ENXIO);
470
if (!ofw_bus_is_compatible(dev, "xlnx,zynqmp-clk"))
471
return (ENXIO);
472
device_set_desc(dev, "ZynqMP Clock Controller");
473
474
return (BUS_PROBE_DEFAULT);
475
}
476
477
static int
478
zynqmp_clock_attach(device_t dev)
479
{
480
struct zynqmp_clock_softc *sc;
481
int rv;
482
483
sc = device_get_softc(dev);
484
sc->dev = dev;
485
sc->parent = device_get_parent(dev);
486
sc->node = ofw_bus_get_node(dev);
487
488
/* Enable all clocks */
489
if (clk_get_by_ofw_name(dev, 0, "pss_ref_clk", &sc->clk_pss_ref) != 0) {
490
device_printf(dev, "Cannot get pss_ref_clk clock\n");
491
return (ENXIO);
492
}
493
rv = clk_enable(sc->clk_pss_ref);
494
if (rv != 0) {
495
device_printf(dev, "Could not enable clock pss_ref_clk\n");
496
return (ENXIO);
497
}
498
if (clk_get_by_ofw_name(dev, 0, "video_clk", &sc->clk_video) != 0) {
499
device_printf(dev, "Cannot get video_clk clock\n");
500
return (ENXIO);
501
}
502
rv = clk_enable(sc->clk_video);
503
if (rv != 0) {
504
device_printf(dev, "Could not enable clock video_clk\n");
505
return (ENXIO);
506
}
507
if (clk_get_by_ofw_name(dev, 0, "pss_alt_ref_clk", &sc->clk_pss_alt_ref) != 0) {
508
device_printf(dev, "Cannot get pss_alt_ref_clk clock\n");
509
return (ENXIO);
510
}
511
rv = clk_enable(sc->clk_pss_alt_ref);
512
if (rv != 0) {
513
device_printf(dev, "Could not enable clock pss_alt_ref_clk\n");
514
return (ENXIO);
515
}
516
if (clk_get_by_ofw_name(dev, 0, "aux_ref_clk", &sc->clk_aux_ref) != 0) {
517
device_printf(dev, "Cannot get pss_aux_clk clock\n");
518
return (ENXIO);
519
}
520
rv = clk_enable(sc->clk_aux_ref);
521
if (rv != 0) {
522
device_printf(dev, "Could not enable clock pss_aux_clk\n");
523
return (ENXIO);
524
}
525
if (clk_get_by_ofw_name(dev, 0, "gt_crx_ref_clk", &sc->clk_gt_crx_ref) != 0) {
526
device_printf(dev, "Cannot get gt_crx_ref_clk clock\n");
527
return (ENXIO);
528
}
529
rv = clk_enable(sc->clk_gt_crx_ref);
530
if (rv != 0) {
531
device_printf(dev, "Could not enable clock gt_crx_ref_clk\n");
532
return (ENXIO);
533
}
534
535
rv = zynqmp_fw_clk_get_all(sc);
536
if (rv != 0) {
537
clk_disable(sc->clk_gt_crx_ref);
538
clk_disable(sc->clk_aux_ref);
539
clk_disable(sc->clk_pss_alt_ref);
540
clk_disable(sc->clk_video);
541
clk_disable(sc->clk_pss_ref);
542
return (rv);
543
}
544
return (0);
545
}
546
547
static device_method_t zynqmp_clock_methods[] = {
548
/* device_if */
549
DEVMETHOD(device_probe, zynqmp_clock_probe),
550
DEVMETHOD(device_attach, zynqmp_clock_attach),
551
552
DEVMETHOD_END
553
};
554
555
static driver_t zynqmp_clock_driver = {
556
"zynqmp_clock",
557
zynqmp_clock_methods,
558
sizeof(struct zynqmp_clock_softc),
559
};
560
561
EARLY_DRIVER_MODULE(zynqmp_clock, simplebus, zynqmp_clock_driver, 0, 0,
562
BUS_PASS_BUS + BUS_PASS_ORDER_LAST);
563
564