xref: /freebsd/sys/arm/ti/clk/ti_dpll_clock.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
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 ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * 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 __FBSDID("$FreeBSD$");
30 
31 #include <sys/param.h>
32 #include <sys/conf.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/systm.h>
37 #include <sys/libkern.h>
38 
39 #include <machine/bus.h>
40 #include <dev/fdt/simplebus.h>
41 
42 #include <dev/extres/clk/clk_div.h>
43 #include <dev/ofw/ofw_bus.h>
44 #include <dev/ofw/ofw_bus_subr.h>
45 
46 #include <arm/ti/clk/ti_clk_dpll.h>
47 #include "clock_common.h"
48 
49 #if 0
50 #define DPRINTF(dev, msg...) device_printf(dev, msg)
51 #else
52 #define DPRINTF(dev, msg...)
53 #endif
54 
55 /*
56  * Devicetree description
57  * Documentation/devicetree/bindings/clock/ti/dpll.txt
58  */
59 
60 struct ti_dpll_softc {
61 	device_t		dev;
62 	uint8_t			dpll_type;
63 
64 	bool			attach_done;
65 	struct ti_clk_dpll_def	dpll_def;
66 
67 	struct clock_cell_info	clock_cell;
68 	struct clkdom		*clkdom;
69 };
70 
71 static int ti_dpll_probe(device_t dev);
72 static int ti_dpll_attach(device_t dev);
73 static int ti_dpll_detach(device_t dev);
74 
75 #define TI_OMAP3_DPLL_CLOCK			17
76 #define TI_OMAP3_DPLL_CORE_CLOCK		16
77 #define TI_OMAP3_DPLL_PER_CLOCK			15
78 #define TI_OMAP3_DPLL_PER_J_TYPE_CLOCK		14
79 #define TI_OMAP4_DPLL_CLOCK			13
80 #define TI_OMAP4_DPLL_X2_CLOCK			12
81 #define TI_OMAP4_DPLL_CORE_CLOCK		11
82 #define TI_OMAP4_DPLL_M4XEN_CLOCK		10
83 #define TI_OMAP4_DPLL_J_TYPE_CLOCK		9
84 #define TI_OMAP5_MPU_DPLL_CLOCK			8
85 #define TI_AM3_DPLL_NO_GATE_CLOCK		7
86 #define TI_AM3_DPLL_J_TYPE_CLOCK		6
87 #define TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK	5
88 #define TI_AM3_DPLL_CLOCK			4
89 #define TI_AM3_DPLL_CORE_CLOCK			3
90 #define TI_AM3_DPLL_X2_CLOCK			2
91 #define TI_OMAP2_DPLL_CORE_CLOCK		1
92 #define TI_DPLL_END				0
93 
94 static struct ofw_compat_data compat_data[] = {
95 	{ "ti,omap3-dpll-clock",	TI_OMAP3_DPLL_CLOCK },
96 	{ "ti,omap3-dpll-core-clock",	TI_OMAP3_DPLL_CORE_CLOCK },
97 	{ "ti,omap3-dpll-per-clock",	TI_OMAP3_DPLL_PER_CLOCK },
98 	{ "ti,omap3-dpll-per-j-type-clock",TI_OMAP3_DPLL_PER_J_TYPE_CLOCK },
99 	{ "ti,omap4-dpll-clock",	TI_OMAP4_DPLL_CLOCK },
100 	{ "ti,omap4-dpll-x2-clock",	TI_OMAP4_DPLL_X2_CLOCK },
101 	{ "ti,omap4-dpll-core-clock",	TI_OMAP4_DPLL_CORE_CLOCK },
102 	{ "ti,omap4-dpll-m4xen-clock",	TI_OMAP4_DPLL_M4XEN_CLOCK },
103 	{ "ti,omap4-dpll-j-type-clock",	TI_OMAP4_DPLL_J_TYPE_CLOCK },
104 	{ "ti,omap5-mpu-dpll-clock",	TI_OMAP5_MPU_DPLL_CLOCK },
105 	{ "ti,am3-dpll-no-gate-clock",	TI_AM3_DPLL_NO_GATE_CLOCK },
106 	{ "ti,am3-dpll-j-type-clock",	TI_AM3_DPLL_J_TYPE_CLOCK },
107 	{ "ti,am3-dpll-no-gate-j-type-clock",TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK },
108 	{ "ti,am3-dpll-clock",		TI_AM3_DPLL_CLOCK },
109 	{ "ti,am3-dpll-core-clock",	TI_AM3_DPLL_CORE_CLOCK },
110 	{ "ti,am3-dpll-x2-clock",	TI_AM3_DPLL_X2_CLOCK },
111 	{ "ti,omap2-dpll-core-clock",	TI_OMAP2_DPLL_CORE_CLOCK },
112 	{ NULL,				TI_DPLL_END }
113 };
114 
115 static int
116 register_clk(struct ti_dpll_softc *sc) {
117 	int err;
118 
119 	sc->clkdom = clkdom_create(sc->dev);
120 	if (sc->clkdom == NULL) {
121 		DPRINTF(sc->dev, "Failed to create clkdom\n");
122 		return (ENXIO);
123 	}
124 
125 	err = ti_clknode_dpll_register(sc->clkdom, &sc->dpll_def);
126 	if (err) {
127 		DPRINTF(sc->dev,
128 			"ti_clknode_dpll_register failed %x\n", err);
129 		return (ENXIO);
130 	}
131 
132 	err = clkdom_finit(sc->clkdom);
133 	if (err) {
134 		DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
135 		return (ENXIO);
136 	}
137 
138 	return (0);
139 }
140 
141 static int
142 ti_dpll_probe(device_t dev)
143 {
144 	if (!ofw_bus_status_okay(dev))
145 		return (ENXIO);
146 
147 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
148 		return (ENXIO);
149 
150 	device_set_desc(dev, "TI DPLL Clock");
151 
152 	return (BUS_PROBE_DEFAULT);
153 }
154 
155 static int
156 parse_dpll_reg(struct ti_dpll_softc *sc) {
157 	ssize_t numbytes_regs;
158 	uint32_t num_regs;
159 	phandle_t node;
160 	cell_t reg_cells[4];
161 
162 	if (sc->dpll_type == TI_AM3_DPLL_X2_CLOCK ||
163 		sc->dpll_type == TI_OMAP4_DPLL_X2_CLOCK) {
164 		sc->dpll_def.ti_clksel_mult.value = 2;
165 		sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_FIXED;
166 
167 		sc->dpll_def.ti_clksel_div.value = 1;
168 		sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_FIXED;
169 		return (0);
170 	}
171 
172 	node = ofw_bus_get_node(sc->dev);
173 
174 	numbytes_regs = OF_getproplen(node, "reg");
175 	num_regs = numbytes_regs / sizeof(cell_t);
176 
177 	/* Sanity check */
178 	if (num_regs > 4)
179 		return (ENXIO);
180 
181 	OF_getencprop(node, "reg", reg_cells, numbytes_regs);
182 
183 	switch (sc->dpll_type) {
184 		case TI_AM3_DPLL_NO_GATE_CLOCK:
185 		case TI_AM3_DPLL_J_TYPE_CLOCK:
186 		case TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK:
187 		case TI_AM3_DPLL_CLOCK:
188 		case TI_AM3_DPLL_CORE_CLOCK:
189 		case TI_AM3_DPLL_X2_CLOCK:
190 			if (num_regs != 3)
191 				return (ENXIO);
192 			sc->dpll_def.ti_clkmode_offset = reg_cells[0];
193 			sc->dpll_def.ti_idlest_offset = reg_cells[1];
194 			sc->dpll_def.ti_clksel_offset = reg_cells[2];
195 			break;
196 
197 		case TI_OMAP2_DPLL_CORE_CLOCK:
198 			if (num_regs != 2)
199 				return (ENXIO);
200 			sc->dpll_def.ti_clkmode_offset = reg_cells[0];
201 			sc->dpll_def.ti_clksel_offset = reg_cells[1];
202 			break;
203 
204 		default:
205 			sc->dpll_def.ti_clkmode_offset = reg_cells[0];
206 			sc->dpll_def.ti_idlest_offset = reg_cells[1];
207 			sc->dpll_def.ti_clksel_offset = reg_cells[2];
208 			sc->dpll_def.ti_autoidle_offset = reg_cells[3];
209 			break;
210 	}
211 
212 	/* AM335x */
213 	if (sc->dpll_def.ti_clksel_offset == CM_CLKSEL_DPLL_PERIPH) {
214 		sc->dpll_def.ti_clksel_mult.shift = 8;
215 		sc->dpll_def.ti_clksel_mult.mask = 0x000FFF00;
216 		sc->dpll_def.ti_clksel_mult.width = 12;
217 		sc->dpll_def.ti_clksel_mult.value = 0;
218 		sc->dpll_def.ti_clksel_mult.min_value = 2;
219 		sc->dpll_def.ti_clksel_mult.max_value = 4095;
220 		sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED |
221 						TI_CLK_FACTOR_MIN_VALUE |
222 						TI_CLK_FACTOR_MAX_VALUE;
223 
224 		sc->dpll_def.ti_clksel_div.shift = 0;
225 		sc->dpll_def.ti_clksel_div.mask = 0x000000FF;
226 		sc->dpll_def.ti_clksel_div.width = 8;
227 		sc->dpll_def.ti_clksel_div.value = 0;
228 		sc->dpll_def.ti_clksel_div.min_value = 0;
229 		sc->dpll_def.ti_clksel_div.max_value = 255;
230 		sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE |
231 						TI_CLK_FACTOR_MAX_VALUE;
232 	} else {
233 		sc->dpll_def.ti_clksel_mult.shift = 8;
234 		sc->dpll_def.ti_clksel_mult.mask = 0x0007FF00;
235 		sc->dpll_def.ti_clksel_mult.width = 11;
236 		sc->dpll_def.ti_clksel_mult.value = 0;
237 		sc->dpll_def.ti_clksel_mult.min_value = 2;
238 		sc->dpll_def.ti_clksel_mult.max_value = 2047;
239 		sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED |
240 						TI_CLK_FACTOR_MIN_VALUE |
241 						TI_CLK_FACTOR_MAX_VALUE;
242 
243 		sc->dpll_def.ti_clksel_div.shift = 0;
244 		sc->dpll_def.ti_clksel_div.mask = 0x0000007F;
245 		sc->dpll_def.ti_clksel_div.width = 7;
246 		sc->dpll_def.ti_clksel_div.value = 0;
247 		sc->dpll_def.ti_clksel_div.min_value = 0;
248 		sc->dpll_def.ti_clksel_div.max_value = 127;
249 		sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE |
250 						TI_CLK_FACTOR_MAX_VALUE;
251 	}
252 	DPRINTF(sc->dev, "clkmode %x idlest %x clksel %x autoidle %x\n",
253 		sc->dpll_def.ti_clkmode_offset, sc->dpll_def.ti_idlest_offset,
254 		sc->dpll_def.ti_clksel_offset,
255 		sc->dpll_def.ti_autoidle_offset);
256 
257 	return (0);
258 }
259 static int
260 ti_dpll_attach(device_t dev)
261 {
262 	struct ti_dpll_softc *sc;
263 	phandle_t node;
264 	int err;
265 
266 	sc = device_get_softc(dev);
267 	sc->dev = dev;
268 
269 	sc->dpll_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
270 	node = ofw_bus_get_node(dev);
271 
272 	/* Grab the content of reg properties */
273 	parse_dpll_reg(sc);
274 
275 	/* default flags (OMAP4&AM335x) not present in the dts at moment */
276 	sc->dpll_def.ti_clkmode_flags = MN_BYPASS_MODE_FLAG | LOCK_MODE_FLAG;
277 
278 	if (OF_hasprop(node, "ti,low-power-stop")) {
279 		sc->dpll_def.ti_clkmode_flags |= LOW_POWER_STOP_MODE_FLAG;
280 	}
281 	if (OF_hasprop(node, "ti,low-power-bypass")) {
282 		sc->dpll_def.ti_clkmode_flags |= IDLE_BYPASS_LOW_POWER_MODE_FLAG;
283 	}
284 	if (OF_hasprop(node, "ti,lock")) {
285 		sc->dpll_def.ti_clkmode_flags |= LOCK_MODE_FLAG;
286 	}
287 
288 	read_clock_cells(sc->dev, &sc->clock_cell);
289 
290 	create_clkdef(sc->dev, &sc->clock_cell, &sc->dpll_def.clkdef);
291 
292 	err = find_parent_clock_names(sc->dev, &sc->clock_cell,
293 			&sc->dpll_def.clkdef);
294 
295 	if (err) {
296 		/* free_clkdef will be called in ti_dpll_new_pass */
297 		DPRINTF(sc->dev, "find_parent_clock_names failed\n");
298 		return (bus_generic_attach(sc->dev));
299 	}
300 
301 	err = register_clk(sc);
302 
303 	if (err) {
304 		/* free_clkdef will be called in ti_dpll_new_pass */
305 		DPRINTF(sc->dev, "register_clk failed\n");
306 		return (bus_generic_attach(sc->dev));
307 	}
308 
309 	sc->attach_done = true;
310 
311 	free_clkdef(&sc->dpll_def.clkdef);
312 
313 	return (bus_generic_attach(sc->dev));
314 }
315 
316 static int
317 ti_dpll_detach(device_t dev)
318 {
319 	return (EBUSY);
320 }
321 
322 static void
323 ti_dpll_new_pass(device_t dev)
324 {
325 	struct ti_dpll_softc *sc;
326 	int err;
327 
328 	sc = device_get_softc(dev);
329 
330 	if (sc->attach_done) {
331 		return;
332 	}
333 
334 	err = find_parent_clock_names(sc->dev, &sc->clock_cell,
335 		&sc->dpll_def.clkdef);
336 	if (err) {
337 		/* free_clkdef will be called in a later call to ti_dpll_new_pass */
338 		DPRINTF(sc->dev,
339 			"new_pass find_parent_clock_names failed\n");
340 		return;
341 	}
342 
343 	err = register_clk(sc);
344 	if (err) {
345 		/* free_clkdef will be called in a later call to ti_dpll_new_pass */
346 		DPRINTF(sc->dev, "new_pass register_clk failed\n");
347 		return;
348 	}
349 
350 	sc->attach_done = true;
351 	free_clkdef(&sc->dpll_def.clkdef);
352 }
353 
354 static device_method_t ti_dpll_methods[] = {
355 	/* Device interface */
356 	DEVMETHOD(device_probe,		ti_dpll_probe),
357 	DEVMETHOD(device_attach,	ti_dpll_attach),
358 	DEVMETHOD(device_detach,	ti_dpll_detach),
359 
360 	/* Bus interface */
361 	DEVMETHOD(bus_new_pass,		ti_dpll_new_pass),
362 
363 	DEVMETHOD_END
364 };
365 
366 DEFINE_CLASS_0(ti_dpll, ti_dpll_driver, ti_dpll_methods,
367 	sizeof(struct ti_dpll_softc));
368 
369 EARLY_DRIVER_MODULE(ti_dpll, simplebus, ti_dpll_driver, 0, 0,
370     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
371 MODULE_VERSION(ti_dpll, 1);
372