xref: /freebsd/sys/arm/ti/clk/ti_gate_clock.c (revision 0050ea241584f931c6089f7b7a7aca3804131397)
1*0050ea24SMichal Meloun /*-
2*0050ea24SMichal Meloun  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*0050ea24SMichal Meloun  *
4*0050ea24SMichal Meloun  * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
5*0050ea24SMichal Meloun  *
6*0050ea24SMichal Meloun  * Redistribution and use in source and binary forms, with or without
7*0050ea24SMichal Meloun  * modification, are permitted provided that the following conditions
8*0050ea24SMichal Meloun  * are met:
9*0050ea24SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
10*0050ea24SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
11*0050ea24SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
12*0050ea24SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
13*0050ea24SMichal Meloun  *    documentation and/or other materials provided with the distribution.
14*0050ea24SMichal Meloun  *
15*0050ea24SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*0050ea24SMichal Meloun  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*0050ea24SMichal Meloun  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*0050ea24SMichal Meloun  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*0050ea24SMichal Meloun  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20*0050ea24SMichal Meloun  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21*0050ea24SMichal Meloun  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22*0050ea24SMichal Meloun  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23*0050ea24SMichal Meloun  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*0050ea24SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*0050ea24SMichal Meloun  * SUCH DAMAGE.
26*0050ea24SMichal Meloun  *
27*0050ea24SMichal Meloun  * $FreeBSD$
28*0050ea24SMichal Meloun  */
29*0050ea24SMichal Meloun 
30*0050ea24SMichal Meloun #include <sys/cdefs.h>
31*0050ea24SMichal Meloun __FBSDID("$FreeBSD$");
32*0050ea24SMichal Meloun 
33*0050ea24SMichal Meloun #include <sys/param.h>
34*0050ea24SMichal Meloun #include <sys/conf.h>
35*0050ea24SMichal Meloun #include <sys/bus.h>
36*0050ea24SMichal Meloun #include <sys/kernel.h>
37*0050ea24SMichal Meloun #include <sys/module.h>
38*0050ea24SMichal Meloun #include <sys/systm.h>
39*0050ea24SMichal Meloun #include <sys/libkern.h>
40*0050ea24SMichal Meloun 
41*0050ea24SMichal Meloun #include <machine/bus.h>
42*0050ea24SMichal Meloun #include <dev/fdt/simplebus.h>
43*0050ea24SMichal Meloun 
44*0050ea24SMichal Meloun #include <dev/extres/clk/clk_gate.h>
45*0050ea24SMichal Meloun #include <dev/ofw/ofw_bus.h>
46*0050ea24SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
47*0050ea24SMichal Meloun 
48*0050ea24SMichal Meloun #include "clock_common.h"
49*0050ea24SMichal Meloun 
50*0050ea24SMichal Meloun #define DEBUG_GATE	0
51*0050ea24SMichal Meloun 
52*0050ea24SMichal Meloun #if DEBUG_GATE
53*0050ea24SMichal Meloun #define DPRINTF(dev, msg...) device_printf(dev, msg)
54*0050ea24SMichal Meloun #else
55*0050ea24SMichal Meloun #define DPRINTF(dev, msg...)
56*0050ea24SMichal Meloun #endif
57*0050ea24SMichal Meloun 
58*0050ea24SMichal Meloun /*
59*0050ea24SMichal Meloun  * Devicetree description
60*0050ea24SMichal Meloun  * Documentation/devicetree/bindings/clock/ti/gate.txt
61*0050ea24SMichal Meloun  */
62*0050ea24SMichal Meloun 
63*0050ea24SMichal Meloun struct ti_gate_softc {
64*0050ea24SMichal Meloun 	device_t		sc_dev;
65*0050ea24SMichal Meloun 	bool			attach_done;
66*0050ea24SMichal Meloun 	uint8_t			sc_type;
67*0050ea24SMichal Meloun 
68*0050ea24SMichal Meloun 	struct clk_gate_def	gate_def;
69*0050ea24SMichal Meloun 	struct clock_cell_info  clock_cell;
70*0050ea24SMichal Meloun 	struct clkdom		*clkdom;
71*0050ea24SMichal Meloun };
72*0050ea24SMichal Meloun 
73*0050ea24SMichal Meloun static int ti_gate_probe(device_t dev);
74*0050ea24SMichal Meloun static int ti_gate_attach(device_t dev);
75*0050ea24SMichal Meloun static int ti_gate_detach(device_t dev);
76*0050ea24SMichal Meloun 
77*0050ea24SMichal Meloun #define TI_GATE_CLOCK			7
78*0050ea24SMichal Meloun #define TI_WAIT_GATE_CLOCK		6
79*0050ea24SMichal Meloun #define TI_DSS_GATE_CLOCK		5
80*0050ea24SMichal Meloun #define TI_AM35XX_GATE_CLOCK		4
81*0050ea24SMichal Meloun #define TI_CLKDM_GATE_CLOCK		3
82*0050ea24SMichal Meloun #define TI_HSDIV_GATE_CLOCK		2
83*0050ea24SMichal Meloun #define TI_COMPOSITE_NO_WAIT_GATE_CLOCK	1
84*0050ea24SMichal Meloun #define TI_GATE_END			0
85*0050ea24SMichal Meloun 
86*0050ea24SMichal Meloun static struct ofw_compat_data compat_data[] = {
87*0050ea24SMichal Meloun 	{ "ti,gate-clock",			TI_GATE_CLOCK },
88*0050ea24SMichal Meloun 	{ "ti,wait-gate-clock",			TI_WAIT_GATE_CLOCK },
89*0050ea24SMichal Meloun 	{ "ti,dss-gate-clock",			TI_DSS_GATE_CLOCK },
90*0050ea24SMichal Meloun 	{ "ti,am35xx-gate-clock",		TI_AM35XX_GATE_CLOCK },
91*0050ea24SMichal Meloun 	{ "ti,clkdm-gate-clock",		TI_CLKDM_GATE_CLOCK },
92*0050ea24SMichal Meloun 	{ "ti,hsdiv-gate-cloc",			TI_HSDIV_GATE_CLOCK },
93*0050ea24SMichal Meloun 	{ "ti,composite-no-wait-gate-clock",	TI_COMPOSITE_NO_WAIT_GATE_CLOCK },
94*0050ea24SMichal Meloun 	{ NULL,					TI_GATE_END }
95*0050ea24SMichal Meloun };
96*0050ea24SMichal Meloun 
97*0050ea24SMichal Meloun static int
98*0050ea24SMichal Meloun ti_gate_probe(device_t dev)
99*0050ea24SMichal Meloun {
100*0050ea24SMichal Meloun 	if (!ofw_bus_status_okay(dev))
101*0050ea24SMichal Meloun 		return (ENXIO);
102*0050ea24SMichal Meloun 
103*0050ea24SMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
104*0050ea24SMichal Meloun 		return (ENXIO);
105*0050ea24SMichal Meloun 
106*0050ea24SMichal Meloun 	device_set_desc(dev, "TI Gate Clock");
107*0050ea24SMichal Meloun 
108*0050ea24SMichal Meloun 	return (BUS_PROBE_DEFAULT);
109*0050ea24SMichal Meloun }
110*0050ea24SMichal Meloun 
111*0050ea24SMichal Meloun static int
112*0050ea24SMichal Meloun register_clk(struct ti_gate_softc *sc) {
113*0050ea24SMichal Meloun 	int err;
114*0050ea24SMichal Meloun 	sc->clkdom = clkdom_create(sc->sc_dev);
115*0050ea24SMichal Meloun 	if (sc->clkdom == NULL) {
116*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "Failed to create clkdom\n");
117*0050ea24SMichal Meloun 		return ENXIO;
118*0050ea24SMichal Meloun 	}
119*0050ea24SMichal Meloun 
120*0050ea24SMichal Meloun 	err = clknode_gate_register(sc->clkdom, &sc->gate_def);
121*0050ea24SMichal Meloun 	if (err) {
122*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "clknode_gate_register failed %x\n", err);
123*0050ea24SMichal Meloun 		return ENXIO;
124*0050ea24SMichal Meloun 	}
125*0050ea24SMichal Meloun 
126*0050ea24SMichal Meloun 	err = clkdom_finit(sc->clkdom);
127*0050ea24SMichal Meloun 	if (err) {
128*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err);
129*0050ea24SMichal Meloun 		return ENXIO;
130*0050ea24SMichal Meloun 	}
131*0050ea24SMichal Meloun 
132*0050ea24SMichal Meloun 	return (0);
133*0050ea24SMichal Meloun }
134*0050ea24SMichal Meloun 
135*0050ea24SMichal Meloun static int
136*0050ea24SMichal Meloun ti_gate_attach(device_t dev)
137*0050ea24SMichal Meloun {
138*0050ea24SMichal Meloun 	struct ti_gate_softc *sc;
139*0050ea24SMichal Meloun 	phandle_t node;
140*0050ea24SMichal Meloun 	int err;
141*0050ea24SMichal Meloun 	cell_t value;
142*0050ea24SMichal Meloun 
143*0050ea24SMichal Meloun 	sc = device_get_softc(dev);
144*0050ea24SMichal Meloun 	sc->sc_dev = dev;
145*0050ea24SMichal Meloun 	node = ofw_bus_get_node(dev);
146*0050ea24SMichal Meloun 
147*0050ea24SMichal Meloun 	/* Get the compatible type */
148*0050ea24SMichal Meloun 	sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
149*0050ea24SMichal Meloun 
150*0050ea24SMichal Meloun 	/* Get the content of reg properties */
151*0050ea24SMichal Meloun 	if (sc->sc_type != TI_CLKDM_GATE_CLOCK) {
152*0050ea24SMichal Meloun 		OF_getencprop(node, "reg", &value, sizeof(value));
153*0050ea24SMichal Meloun 		sc->gate_def.offset = value;
154*0050ea24SMichal Meloun 	}
155*0050ea24SMichal Meloun #if DEBUG_GATE
156*0050ea24SMichal Meloun 	else {
157*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "no reg (TI_CLKDM_GATE_CLOCK)\n");
158*0050ea24SMichal Meloun 	}
159*0050ea24SMichal Meloun #endif
160*0050ea24SMichal Meloun 
161*0050ea24SMichal Meloun 	if (OF_hasprop(node, "ti,bit-shift")) {
162*0050ea24SMichal Meloun 		OF_getencprop(node, "ti,bit-shift", &value, sizeof(value));
163*0050ea24SMichal Meloun 		sc->gate_def.shift = value;
164*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->gate_def.shift);
165*0050ea24SMichal Meloun 	}
166*0050ea24SMichal Meloun 	if (OF_hasprop(node, "ti,set-bit-to-disable")) {
167*0050ea24SMichal Meloun 		sc->gate_def.on_value = 0;
168*0050ea24SMichal Meloun 		sc->gate_def.off_value = 1;
169*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev,
170*0050ea24SMichal Meloun 			"on_value = 0, off_value = 1 (ti,set-bit-to-disable)\n");
171*0050ea24SMichal Meloun 	} else {
172*0050ea24SMichal Meloun 		sc->gate_def.on_value = 1;
173*0050ea24SMichal Meloun 		sc->gate_def.off_value = 0;
174*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "on_value = 1, off_value = 0\n");
175*0050ea24SMichal Meloun 	}
176*0050ea24SMichal Meloun 
177*0050ea24SMichal Meloun 	sc->gate_def.gate_flags = 0x0;
178*0050ea24SMichal Meloun 
179*0050ea24SMichal Meloun 	read_clock_cells(sc->sc_dev, &sc->clock_cell);
180*0050ea24SMichal Meloun 
181*0050ea24SMichal Meloun 	create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
182*0050ea24SMichal Meloun 
183*0050ea24SMichal Meloun 	/* Calculate mask */
184*0050ea24SMichal Meloun 	sc->gate_def.mask = (1 << fls(sc->clock_cell.num_real_clocks)) - 1;
185*0050ea24SMichal Meloun 	DPRINTF(sc->sc_dev, "num_real_clocks %x gate_def.mask %x\n",
186*0050ea24SMichal Meloun 		sc->clock_cell.num_real_clocks, sc->gate_def.mask);
187*0050ea24SMichal Meloun 
188*0050ea24SMichal Meloun 	err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
189*0050ea24SMichal Meloun 
190*0050ea24SMichal Meloun 	if (err) {
191*0050ea24SMichal Meloun 		/* free_clkdef will be called in ti_gate_new_pass */
192*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n");
193*0050ea24SMichal Meloun 		return (bus_generic_attach(sc->sc_dev));
194*0050ea24SMichal Meloun 	}
195*0050ea24SMichal Meloun 
196*0050ea24SMichal Meloun 	err = register_clk(sc);
197*0050ea24SMichal Meloun 
198*0050ea24SMichal Meloun 	if (err) {
199*0050ea24SMichal Meloun 		/* free_clkdef will be called in ti_gate_new_pass */
200*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "register_clk failed\n");
201*0050ea24SMichal Meloun 		return (bus_generic_attach(sc->sc_dev));
202*0050ea24SMichal Meloun 	}
203*0050ea24SMichal Meloun 
204*0050ea24SMichal Meloun 	sc->attach_done = true;
205*0050ea24SMichal Meloun 
206*0050ea24SMichal Meloun 	free_clkdef(&sc->gate_def.clkdef);
207*0050ea24SMichal Meloun 
208*0050ea24SMichal Meloun 	return (bus_generic_attach(sc->sc_dev));
209*0050ea24SMichal Meloun }
210*0050ea24SMichal Meloun 
211*0050ea24SMichal Meloun static int
212*0050ea24SMichal Meloun ti_gate_detach(device_t dev)
213*0050ea24SMichal Meloun {
214*0050ea24SMichal Meloun 	return (EBUSY);
215*0050ea24SMichal Meloun }
216*0050ea24SMichal Meloun 
217*0050ea24SMichal Meloun static void
218*0050ea24SMichal Meloun ti_gate_new_pass(device_t dev) {
219*0050ea24SMichal Meloun 	struct ti_gate_softc *sc;
220*0050ea24SMichal Meloun 	int err;
221*0050ea24SMichal Meloun 
222*0050ea24SMichal Meloun 	sc = device_get_softc(dev);
223*0050ea24SMichal Meloun 
224*0050ea24SMichal Meloun 	if (sc->attach_done) {
225*0050ea24SMichal Meloun 		return;
226*0050ea24SMichal Meloun 	}
227*0050ea24SMichal Meloun 
228*0050ea24SMichal Meloun 	err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->gate_def.clkdef);
229*0050ea24SMichal Meloun 	if (err) {
230*0050ea24SMichal Meloun 		/* free_clkdef will be called in later call to ti_gate_new_pass */
231*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "new_pass find_parent_clock_names failed\n");
232*0050ea24SMichal Meloun 		return;
233*0050ea24SMichal Meloun 	}
234*0050ea24SMichal Meloun 
235*0050ea24SMichal Meloun 	err = register_clk(sc);
236*0050ea24SMichal Meloun 	if (err) {
237*0050ea24SMichal Meloun 		/* free_clkdef will be called in later call to ti_gate_new_pass */
238*0050ea24SMichal Meloun 		DPRINTF(sc->sc_dev, "new_pass register_clk failed\n");
239*0050ea24SMichal Meloun 		return;
240*0050ea24SMichal Meloun 	}
241*0050ea24SMichal Meloun 
242*0050ea24SMichal Meloun 	sc->attach_done = true;
243*0050ea24SMichal Meloun 
244*0050ea24SMichal Meloun 	free_clkdef(&sc->gate_def.clkdef);
245*0050ea24SMichal Meloun }
246*0050ea24SMichal Meloun 
247*0050ea24SMichal Meloun static device_method_t ti_gate_methods[] = {
248*0050ea24SMichal Meloun 	/* Device interface */
249*0050ea24SMichal Meloun 	DEVMETHOD(device_probe,		ti_gate_probe),
250*0050ea24SMichal Meloun 	DEVMETHOD(device_attach,	ti_gate_attach),
251*0050ea24SMichal Meloun 	DEVMETHOD(device_detach,	ti_gate_detach),
252*0050ea24SMichal Meloun 
253*0050ea24SMichal Meloun 	/* Bus interface */
254*0050ea24SMichal Meloun 	DEVMETHOD(bus_new_pass,		ti_gate_new_pass),
255*0050ea24SMichal Meloun 
256*0050ea24SMichal Meloun 	DEVMETHOD_END
257*0050ea24SMichal Meloun };
258*0050ea24SMichal Meloun 
259*0050ea24SMichal Meloun DEFINE_CLASS_0(ti_gate, ti_gate_driver, ti_gate_methods,
260*0050ea24SMichal Meloun 	sizeof(struct ti_gate_softc));
261*0050ea24SMichal Meloun 
262*0050ea24SMichal Meloun static devclass_t ti_gate_devclass;
263*0050ea24SMichal Meloun 
264*0050ea24SMichal Meloun EARLY_DRIVER_MODULE(ti_gate, simplebus, ti_gate_driver,
265*0050ea24SMichal Meloun 	ti_gate_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
266*0050ea24SMichal Meloun MODULE_VERSION(ti_gate, 1);
267