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