xref: /freebsd/sys/dev/clk/xilinx/zynqmp_clock.c (revision ddfc6f84f24215b418af19260e9156219f6df03e)
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