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
zynqmp_clk_init(struct clknode * clk,device_t dev)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
zynqmp_clk_register(struct clkdom * clkdom,device_t fw,struct zynqmp_clk * clkdef)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
zynqmp_fw_clk_get_name(struct zynqmp_clock_softc * sc,struct zynqmp_clk * clk,uint32_t id)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
zynqmp_fw_clk_get_attributes(struct zynqmp_clock_softc * sc,struct zynqmp_clk * clk,uint32_t id)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
zynqmp_fw_clk_get_parents(struct zynqmp_clock_softc * sc,struct zynqmp_clk * clk,uint32_t id)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
zynqmp_fw_clk_get_topology(struct zynqmp_clock_softc * sc,struct zynqmp_clk * clk,uint32_t id)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
zynqmp_clock_ofw_map(struct clkdom * clkdom,uint32_t ncells,phandle_t * cells,struct clknode ** clk)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
zynqmp_fw_clk_get_all(struct zynqmp_clock_softc * sc)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
zynqmp_clock_probe(device_t dev)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
zynqmp_clock_attach(device_t dev)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