xref: /freebsd/sys/dev/firmware/arm/scmi_clk.c (revision 35f93203963f83161012cd731e858a56548c2ef9)
154b96380SRuslan Bukin /*-
254b96380SRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
354b96380SRuslan Bukin  *
454b96380SRuslan Bukin  * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
5*35f93203SCristian Marussi  * Copyright (c) 2023 Arm Ltd
654b96380SRuslan Bukin  *
754b96380SRuslan Bukin  * This work was supported by Innovate UK project 105694, "Digital Security
854b96380SRuslan Bukin  * by Design (DSbD) Technology Platform Prototype".
954b96380SRuslan Bukin  *
1054b96380SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
1154b96380SRuslan Bukin  * modification, are permitted provided that the following conditions
1254b96380SRuslan Bukin  * are met:
1354b96380SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
1454b96380SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
1554b96380SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
1654b96380SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
1754b96380SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
1854b96380SRuslan Bukin  *
1954b96380SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2054b96380SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2154b96380SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2254b96380SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2354b96380SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2454b96380SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2554b96380SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2654b96380SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2754b96380SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2854b96380SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2954b96380SRuslan Bukin  * SUCH DAMAGE.
3054b96380SRuslan Bukin  */
3154b96380SRuslan Bukin 
3254b96380SRuslan Bukin #include <sys/param.h>
3354b96380SRuslan Bukin #include <sys/systm.h>
3454b96380SRuslan Bukin #include <sys/bus.h>
3554b96380SRuslan Bukin #include <sys/cpu.h>
3654b96380SRuslan Bukin #include <sys/kernel.h>
3754b96380SRuslan Bukin #include <sys/module.h>
3854b96380SRuslan Bukin 
39be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
4054b96380SRuslan Bukin #include <dev/fdt/simplebus.h>
4154b96380SRuslan Bukin #include <dev/fdt/fdt_common.h>
4254b96380SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
4354b96380SRuslan Bukin 
4454b96380SRuslan Bukin #include "scmi.h"
4554b96380SRuslan Bukin #include "scmi_protocols.h"
4654b96380SRuslan Bukin #include "scmi_clk.h"
4754b96380SRuslan Bukin 
4854b96380SRuslan Bukin struct scmi_clk_softc {
4954b96380SRuslan Bukin 	device_t	dev;
5054b96380SRuslan Bukin 	device_t	scmi;
5154b96380SRuslan Bukin 	struct clkdom	*clkdom;
5254b96380SRuslan Bukin };
5354b96380SRuslan Bukin 
5454b96380SRuslan Bukin struct scmi_clknode_softc {
5554b96380SRuslan Bukin 	device_t	dev;
5654b96380SRuslan Bukin 	int		clock_id;
5754b96380SRuslan Bukin };
5854b96380SRuslan Bukin 
59ecd8cc84SCristian Marussi static int
6054b96380SRuslan Bukin scmi_clk_get_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t *rate)
6154b96380SRuslan Bukin {
62*35f93203SCristian Marussi 	struct scmi_clk_rate_get_out *out;
63*35f93203SCristian Marussi 	struct scmi_clk_rate_get_in *in;
6454b96380SRuslan Bukin 	int error;
6554b96380SRuslan Bukin 
66*35f93203SCristian Marussi 	in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
67*35f93203SCristian Marussi 	    SCMI_CLOCK_RATE_GET, sizeof(*in), sizeof(*out));
68*35f93203SCristian Marussi 	if (in == NULL)
6954b96380SRuslan Bukin 		return (ENXIO);
7054b96380SRuslan Bukin 
71*35f93203SCristian Marussi 	in->clock_id = clk_id;
72*35f93203SCristian Marussi 	error = scmi_request(sc->scmi, in, (void **)&out);
73*35f93203SCristian Marussi 	if (error == 0)
74*35f93203SCristian Marussi 		*rate = out->rate_lsb | ((uint64_t)out->rate_msb << 32);
7554b96380SRuslan Bukin 
76*35f93203SCristian Marussi 	scmi_buf_put(sc->scmi, in);
77*35f93203SCristian Marussi 
78*35f93203SCristian Marussi 	return (error);
7954b96380SRuslan Bukin }
8054b96380SRuslan Bukin 
8154b96380SRuslan Bukin static int
8254b96380SRuslan Bukin scmi_clk_set_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t rate)
8354b96380SRuslan Bukin {
84*35f93203SCristian Marussi 	struct scmi_clk_rate_set_in *in;
85*35f93203SCristian Marussi 	void *out;
8654b96380SRuslan Bukin 	int error;
8754b96380SRuslan Bukin 
88*35f93203SCristian Marussi 	in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
89*35f93203SCristian Marussi 	    SCMI_CLOCK_RATE_SET, sizeof(*in), 0);
90*35f93203SCristian Marussi 	if (in == NULL)
9154b96380SRuslan Bukin 		return (ENXIO);
9254b96380SRuslan Bukin 
93*35f93203SCristian Marussi 	in->clock_id = clk_id;
94*35f93203SCristian Marussi 	in->flags = SCMI_CLK_RATE_ROUND_CLOSEST;
95*35f93203SCristian Marussi 	in->rate_lsb = (uint32_t)rate;
96*35f93203SCristian Marussi 	in->rate_msb = (uint32_t)(rate >> 32);
97*35f93203SCristian Marussi 
98*35f93203SCristian Marussi 	error = scmi_request(sc->scmi, in, &out);
99*35f93203SCristian Marussi 
100*35f93203SCristian Marussi 	scmi_buf_put(sc->scmi, in);
101*35f93203SCristian Marussi 
102*35f93203SCristian Marussi 	return (error);
10354b96380SRuslan Bukin }
10454b96380SRuslan Bukin 
10519a7cf0cSMark Johnston static int __unused
10654b96380SRuslan Bukin scmi_clk_gate(struct scmi_clk_softc *sc, int clk_id, int enable)
10754b96380SRuslan Bukin {
108*35f93203SCristian Marussi 	struct scmi_clk_state_in *in;
109*35f93203SCristian Marussi 	void *out;
11054b96380SRuslan Bukin 	int error;
11154b96380SRuslan Bukin 
112*35f93203SCristian Marussi 	in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
113*35f93203SCristian Marussi 	    SCMI_CLOCK_CONFIG_SET, sizeof(*in), 0);
114*35f93203SCristian Marussi 	if (in == NULL)
11554b96380SRuslan Bukin 		return (ENXIO);
11654b96380SRuslan Bukin 
117*35f93203SCristian Marussi 	in->clock_id = clk_id;
118*35f93203SCristian Marussi 	in->attributes = enable;
119*35f93203SCristian Marussi 	error = scmi_request(sc->scmi, in, &out);
120*35f93203SCristian Marussi 
121*35f93203SCristian Marussi 	scmi_buf_put(sc->scmi, in);
122*35f93203SCristian Marussi 
123*35f93203SCristian Marussi 	return (error);
12454b96380SRuslan Bukin }
12554b96380SRuslan Bukin 
12654b96380SRuslan Bukin static int
12754b96380SRuslan Bukin scmi_clknode_init(struct clknode *clk, device_t dev)
12854b96380SRuslan Bukin {
12954b96380SRuslan Bukin 
13054b96380SRuslan Bukin 	clknode_init_parent_idx(clk, 0);
13154b96380SRuslan Bukin 
13254b96380SRuslan Bukin 	return (0);
13354b96380SRuslan Bukin }
13454b96380SRuslan Bukin 
13554b96380SRuslan Bukin static int
13654b96380SRuslan Bukin scmi_clknode_recalc_freq(struct clknode *clk, uint64_t *freq)
13754b96380SRuslan Bukin {
138ecd8cc84SCristian Marussi 	struct scmi_clknode_softc *clk_sc;
139ecd8cc84SCristian Marussi 	struct scmi_clk_softc *sc;
140ecd8cc84SCristian Marussi 	uint64_t rate;
141ecd8cc84SCristian Marussi 	int ret;
14254b96380SRuslan Bukin 
143ecd8cc84SCristian Marussi 	clk_sc = clknode_get_softc(clk);
144ecd8cc84SCristian Marussi 	sc = device_get_softc(clk_sc->dev);
145ecd8cc84SCristian Marussi 	ret = scmi_clk_get_rate(sc, clk_sc->clock_id, &rate);
146ecd8cc84SCristian Marussi 	if (ret == 0)
147ecd8cc84SCristian Marussi 		*freq = rate;
148ecd8cc84SCristian Marussi 
149ecd8cc84SCristian Marussi 	return (ret);
15054b96380SRuslan Bukin }
15154b96380SRuslan Bukin 
15254b96380SRuslan Bukin static int
15354b96380SRuslan Bukin scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
15454b96380SRuslan Bukin     int flags, int *stop)
15554b96380SRuslan Bukin {
15654b96380SRuslan Bukin 	struct scmi_clknode_softc *clk_sc;
15754b96380SRuslan Bukin 	struct scmi_clk_softc *sc;
15854b96380SRuslan Bukin 
15954b96380SRuslan Bukin 	clk_sc = clknode_get_softc(clk);
16054b96380SRuslan Bukin 	sc = device_get_softc(clk_sc->dev);
16154b96380SRuslan Bukin 
16254b96380SRuslan Bukin 	scmi_clk_set_rate(sc, clk_sc->clock_id, *fout);
16354b96380SRuslan Bukin 
16454b96380SRuslan Bukin 	*stop = 1;
16554b96380SRuslan Bukin 
16654b96380SRuslan Bukin 	return (0);
16754b96380SRuslan Bukin }
16854b96380SRuslan Bukin 
16954b96380SRuslan Bukin static clknode_method_t scmi_clknode_methods[] = {
17054b96380SRuslan Bukin 	/* Device interface */
17154b96380SRuslan Bukin 	CLKNODEMETHOD(clknode_init,		scmi_clknode_init),
17254b96380SRuslan Bukin 	CLKNODEMETHOD(clknode_recalc_freq,	scmi_clknode_recalc_freq),
17354b96380SRuslan Bukin 	CLKNODEMETHOD(clknode_set_freq,		scmi_clknode_set_freq),
17454b96380SRuslan Bukin 	CLKNODEMETHOD_END
17554b96380SRuslan Bukin };
17654b96380SRuslan Bukin 
17754b96380SRuslan Bukin DEFINE_CLASS_1(scmi_clknode, scmi_clknode_class, scmi_clknode_methods,
17854b96380SRuslan Bukin     sizeof(struct scmi_clknode_softc), clknode_class);
17954b96380SRuslan Bukin 
18054b96380SRuslan Bukin static int
18154b96380SRuslan Bukin scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name)
18254b96380SRuslan Bukin {
18354b96380SRuslan Bukin 	struct scmi_clknode_softc *clk_sc;
18454b96380SRuslan Bukin 	struct clknode_init_def def;
18554b96380SRuslan Bukin 	struct clknode *clk;
18654b96380SRuslan Bukin 
18754b96380SRuslan Bukin 	memset(&def, 0, sizeof(def));
18854b96380SRuslan Bukin 	def.id = index;
18954b96380SRuslan Bukin 	def.name = clock_name;
19054b96380SRuslan Bukin 	def.parent_names = NULL;
19154b96380SRuslan Bukin 	def.parent_cnt = 0;
19254b96380SRuslan Bukin 
19354b96380SRuslan Bukin 	clk = clknode_create(sc->clkdom, &scmi_clknode_class, &def);
19454b96380SRuslan Bukin 	if (clk == NULL) {
19554b96380SRuslan Bukin 		device_printf(sc->dev, "Cannot create clknode.\n");
19654b96380SRuslan Bukin 		return (ENXIO);
19754b96380SRuslan Bukin 	}
19854b96380SRuslan Bukin 
19954b96380SRuslan Bukin 	clk_sc = clknode_get_softc(clk);
20054b96380SRuslan Bukin 	clk_sc->dev = sc->dev;
20154b96380SRuslan Bukin 	clk_sc->clock_id = index;
20254b96380SRuslan Bukin 
20354b96380SRuslan Bukin 	if (clknode_register(sc->clkdom, clk) == NULL) {
20454b96380SRuslan Bukin 		device_printf(sc->dev, "Could not register clock '%s'.\n",
20554b96380SRuslan Bukin 		    def.name);
20654b96380SRuslan Bukin 		return (ENXIO);
20754b96380SRuslan Bukin 	}
20854b96380SRuslan Bukin 
20954b96380SRuslan Bukin 	device_printf(sc->dev, "Clock '%s' registered.\n", def.name);
21054b96380SRuslan Bukin 
21154b96380SRuslan Bukin 	return (0);
21254b96380SRuslan Bukin }
21354b96380SRuslan Bukin 
21454b96380SRuslan Bukin static int
21554b96380SRuslan Bukin scmi_clk_get_name(struct scmi_clk_softc *sc, int index, char **result)
21654b96380SRuslan Bukin {
217*35f93203SCristian Marussi 	struct scmi_clk_name_get_out *out;
218*35f93203SCristian Marussi 	struct scmi_clk_name_get_in *in;
21954b96380SRuslan Bukin 	int error;
22054b96380SRuslan Bukin 
221*35f93203SCristian Marussi 	in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
222*35f93203SCristian Marussi 	    SCMI_CLOCK_NAME_GET, sizeof(*in), sizeof(*out));
223*35f93203SCristian Marussi 	if (in == NULL)
22454b96380SRuslan Bukin 		return (ENXIO);
22554b96380SRuslan Bukin 
226*35f93203SCristian Marussi 	in->clock_id = index;
227*35f93203SCristian Marussi 	error = scmi_request(sc->scmi, in, (void **)&out);
228*35f93203SCristian Marussi 	if (error == 0) {
229*35f93203SCristian Marussi 		char *clock_name;
23054b96380SRuslan Bukin 
231*35f93203SCristian Marussi 		clock_name = malloc(sizeof(out->name), M_DEVBUF, M_WAITOK);
232*35f93203SCristian Marussi 		strncpy(clock_name, out->name, sizeof(out->name));
23354b96380SRuslan Bukin 		*result = clock_name;
234*35f93203SCristian Marussi 	}
23554b96380SRuslan Bukin 
236*35f93203SCristian Marussi 	scmi_buf_put(sc->scmi, in);
237*35f93203SCristian Marussi 
238*35f93203SCristian Marussi 	return (error);
23954b96380SRuslan Bukin }
24054b96380SRuslan Bukin 
24154b96380SRuslan Bukin static int
24254b96380SRuslan Bukin scmi_clk_attrs(struct scmi_clk_softc *sc, int index)
24354b96380SRuslan Bukin {
244*35f93203SCristian Marussi 	struct scmi_clk_attrs_out *out;
245*35f93203SCristian Marussi 	struct scmi_clk_attrs_in *in;
24654b96380SRuslan Bukin 	char *clock_name;
247*35f93203SCristian Marussi 	int error;
24854b96380SRuslan Bukin 
249*35f93203SCristian Marussi 	in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
250*35f93203SCristian Marussi 	    SCMI_CLOCK_ATTRIBUTES, sizeof(*in), sizeof(*out));
251*35f93203SCristian Marussi 	if (in == NULL)
25254b96380SRuslan Bukin 		return (ENXIO);
25354b96380SRuslan Bukin 
254*35f93203SCristian Marussi 	in->clock_id = index;
255*35f93203SCristian Marussi 	error = scmi_request(sc->scmi, in, (void **)&out);
256*35f93203SCristian Marussi 	if (error == 0) {
257*35f93203SCristian Marussi 		if (out->attributes & CLK_ATTRS_EXT_CLK_NAME) {
25854b96380SRuslan Bukin 			error = scmi_clk_get_name(sc, index, &clock_name);
25954b96380SRuslan Bukin 		} else {
260*35f93203SCristian Marussi 			clock_name = malloc(sizeof(out->clock_name),
261*35f93203SCristian Marussi 			    M_DEVBUF, M_WAITOK);
262*35f93203SCristian Marussi 			strncpy(clock_name, out->clock_name,
263*35f93203SCristian Marussi 			    sizeof(out->clock_name));
26454b96380SRuslan Bukin 		}
26554b96380SRuslan Bukin 
266*35f93203SCristian Marussi 		if (error == 0)
26754b96380SRuslan Bukin 			error = scmi_clk_add_node(sc, index, clock_name);
268*35f93203SCristian Marussi 	}
269*35f93203SCristian Marussi 
270*35f93203SCristian Marussi 	scmi_buf_put(sc->scmi, in);
27154b96380SRuslan Bukin 
27254b96380SRuslan Bukin 	return (error);
27354b96380SRuslan Bukin }
27454b96380SRuslan Bukin 
27554b96380SRuslan Bukin static int
27654b96380SRuslan Bukin scmi_clk_discover(struct scmi_clk_softc *sc)
27754b96380SRuslan Bukin {
278*35f93203SCristian Marussi 	struct scmi_clk_protocol_attrs_out *out;
279*35f93203SCristian Marussi 	void *in;
28054b96380SRuslan Bukin 	int nclocks;
28154b96380SRuslan Bukin 	int failing;
28254b96380SRuslan Bukin 	int error;
28354b96380SRuslan Bukin 	int i;
28454b96380SRuslan Bukin 
285*35f93203SCristian Marussi 	in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
286*35f93203SCristian Marussi 	    SCMI_PROTOCOL_ATTRIBUTES, 0, sizeof(*out));
287*35f93203SCristian Marussi 	if (in == NULL)
28854b96380SRuslan Bukin 		return (ENXIO);
28954b96380SRuslan Bukin 
290*35f93203SCristian Marussi 	error = scmi_request(sc->scmi, in, (void **)&out);
291*35f93203SCristian Marussi 	if (error == 0) {
292*35f93203SCristian Marussi 		nclocks = (out->attributes & CLK_ATTRS_NCLOCKS_M) >>
29354b96380SRuslan Bukin 		    CLK_ATTRS_NCLOCKS_S;
29454b96380SRuslan Bukin 
29554b96380SRuslan Bukin 		device_printf(sc->dev, "Found %d clocks.\n", nclocks);
29654b96380SRuslan Bukin 
29754b96380SRuslan Bukin 		failing = 0;
29854b96380SRuslan Bukin 
29954b96380SRuslan Bukin 		for (i = 0; i < nclocks; i++) {
30054b96380SRuslan Bukin 			error = scmi_clk_attrs(sc, i);
30154b96380SRuslan Bukin 			if (error) {
30254b96380SRuslan Bukin 				device_printf(sc->dev,
30354b96380SRuslan Bukin 				    "Could not process clock index %d.\n", i);
30454b96380SRuslan Bukin 				failing++;
305*35f93203SCristian Marussi 				error = 0;
30654b96380SRuslan Bukin 			}
30754b96380SRuslan Bukin 		}
30854b96380SRuslan Bukin 		if (failing == nclocks)
309*35f93203SCristian Marussi 			error = ENXIO;
310*35f93203SCristian Marussi 	} else {
311*35f93203SCristian Marussi 		error = ENXIO;
312*35f93203SCristian Marussi 	}
31354b96380SRuslan Bukin 
314*35f93203SCristian Marussi 	scmi_buf_put(sc->scmi, in);
315*35f93203SCristian Marussi 
316*35f93203SCristian Marussi 	return (error);
31754b96380SRuslan Bukin }
31854b96380SRuslan Bukin 
31954b96380SRuslan Bukin static int
32054b96380SRuslan Bukin scmi_clk_init(struct scmi_clk_softc *sc)
32154b96380SRuslan Bukin {
32254b96380SRuslan Bukin 	int error;
32354b96380SRuslan Bukin 
32454b96380SRuslan Bukin 	/* Create clock domain */
32554b96380SRuslan Bukin 	sc->clkdom = clkdom_create(sc->dev);
32654b96380SRuslan Bukin 	if (sc->clkdom == NULL)
32754b96380SRuslan Bukin 		return (ENXIO);
32854b96380SRuslan Bukin 
32954b96380SRuslan Bukin 	error = scmi_clk_discover(sc);
33054b96380SRuslan Bukin 	if (error) {
33154b96380SRuslan Bukin 		device_printf(sc->dev, "Could not discover clocks.\n");
33254b96380SRuslan Bukin 		return (ENXIO);
33354b96380SRuslan Bukin 	}
33454b96380SRuslan Bukin 
33554b96380SRuslan Bukin 	error = clkdom_finit(sc->clkdom);
33654b96380SRuslan Bukin 	if (error) {
33754b96380SRuslan Bukin 		device_printf(sc->dev, "Failed to init clock domain.\n");
33854b96380SRuslan Bukin 		return (ENXIO);
33954b96380SRuslan Bukin 	}
34054b96380SRuslan Bukin 
34154b96380SRuslan Bukin 	return (0);
34254b96380SRuslan Bukin }
34354b96380SRuslan Bukin 
34454b96380SRuslan Bukin static int
34554b96380SRuslan Bukin scmi_clk_probe(device_t dev)
34654b96380SRuslan Bukin {
34754b96380SRuslan Bukin 	phandle_t node;
34854b96380SRuslan Bukin 	uint32_t reg;
34954b96380SRuslan Bukin 	int error;
35054b96380SRuslan Bukin 
35154b96380SRuslan Bukin 	node = ofw_bus_get_node(dev);
35254b96380SRuslan Bukin 
35354b96380SRuslan Bukin 	error = OF_getencprop(node, "reg", &reg, sizeof(uint32_t));
35454b96380SRuslan Bukin 	if (error < 0)
35554b96380SRuslan Bukin 		return (ENXIO);
35654b96380SRuslan Bukin 
35754b96380SRuslan Bukin 	if (reg != SCMI_PROTOCOL_ID_CLOCK)
35854b96380SRuslan Bukin 		return (ENXIO);
35954b96380SRuslan Bukin 
36054b96380SRuslan Bukin 	device_set_desc(dev, "SCMI Clock Management Unit");
36154b96380SRuslan Bukin 
36254b96380SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
36354b96380SRuslan Bukin }
36454b96380SRuslan Bukin 
36554b96380SRuslan Bukin static int
36654b96380SRuslan Bukin scmi_clk_attach(device_t dev)
36754b96380SRuslan Bukin {
36854b96380SRuslan Bukin 	struct scmi_clk_softc *sc;
36954b96380SRuslan Bukin 	phandle_t node;
37054b96380SRuslan Bukin 
37154b96380SRuslan Bukin 	sc = device_get_softc(dev);
37254b96380SRuslan Bukin 	sc->dev = dev;
37354b96380SRuslan Bukin 	sc->scmi = device_get_parent(dev);
37454b96380SRuslan Bukin 
37554b96380SRuslan Bukin 	node = ofw_bus_get_node(sc->dev);
37654b96380SRuslan Bukin 
37754b96380SRuslan Bukin 	OF_device_register_xref(OF_xref_from_node(node), sc->dev);
37854b96380SRuslan Bukin 
37954b96380SRuslan Bukin 	scmi_clk_init(sc);
38054b96380SRuslan Bukin 
38154b96380SRuslan Bukin 	return (0);
38254b96380SRuslan Bukin }
38354b96380SRuslan Bukin 
38454b96380SRuslan Bukin static int
38554b96380SRuslan Bukin scmi_clk_detach(device_t dev)
38654b96380SRuslan Bukin {
38754b96380SRuslan Bukin 
38854b96380SRuslan Bukin 	return (0);
38954b96380SRuslan Bukin }
39054b96380SRuslan Bukin 
39154b96380SRuslan Bukin static device_method_t scmi_clk_methods[] = {
39254b96380SRuslan Bukin 	/* Device interface */
39354b96380SRuslan Bukin 	DEVMETHOD(device_probe,		scmi_clk_probe),
39454b96380SRuslan Bukin 	DEVMETHOD(device_attach,	scmi_clk_attach),
39554b96380SRuslan Bukin 	DEVMETHOD(device_detach,	scmi_clk_detach),
39654b96380SRuslan Bukin 	DEVMETHOD_END
39754b96380SRuslan Bukin };
39854b96380SRuslan Bukin 
39954b96380SRuslan Bukin static driver_t scmi_clk_driver = {
40054b96380SRuslan Bukin 	"scmi_clk",
40154b96380SRuslan Bukin 	scmi_clk_methods,
40254b96380SRuslan Bukin 	sizeof(struct scmi_clk_softc),
40354b96380SRuslan Bukin };
40454b96380SRuslan Bukin 
40554b96380SRuslan Bukin EARLY_DRIVER_MODULE(scmi_clk, scmi, scmi_clk_driver, 0, 0,
40654b96380SRuslan Bukin     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
40754b96380SRuslan Bukin MODULE_VERSION(scmi_clk, 1);
408