xref: /freebsd/sys/arm/ti/clk/ti_clkctrl.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
5  *
6  * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/fbio.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/rman.h>
37 #include <sys/resource.h>
38 #include <machine/bus.h>
39 #include <vm/vm.h>
40 #include <vm/vm_extern.h>
41 #include <vm/vm_kern.h>
42 #include <vm/pmap.h>
43 
44 #include <dev/fdt/simplebus.h>
45 
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48 
49 #include <arm/ti/clk/ti_clk_clkctrl.h>
50 #include <arm/ti/ti_omap4_cm.h>
51 #include <arm/ti/ti_cpuid.h>
52 
53 #if 0
54 #define DPRINTF(dev, msg...) device_printf(dev, msg)
55 #else
56 #define DPRINTF(dev, msg...)
57 #endif
58 
59 #define L4LS_CLKCTRL_38 	2
60 #define L4_WKUP_CLKCTRL_0	1
61 #define NO_SPECIAL_REG		0
62 
63 /* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */
64 
65 #define TI_CLKCTRL_L4_WKUP	5
66 #define TI_CLKCTRL_L4_SECURE	4
67 #define TI_CLKCTRL_L4_PER	3
68 #define TI_CLKCTRL_L4_CFG	2
69 #define TI_CLKCTRL		1
70 #define TI_CLKCTRL_END		0
71 
72 static struct ofw_compat_data compat_data[] = {
73 	{ "ti,clkctrl-l4-wkup",		TI_CLKCTRL_L4_WKUP },
74 	{ "ti,clkctrl-l4-secure",	TI_CLKCTRL_L4_SECURE },
75 	{ "ti,clkctrl-l4-per",		TI_CLKCTRL_L4_PER },
76 	{ "ti,clkctrl-l4-cfg",		TI_CLKCTRL_L4_CFG },
77 	{ "ti,clkctrl",			TI_CLKCTRL },
78 	{ NULL,				TI_CLKCTRL_END }
79 };
80 
81 struct ti_clkctrl_softc {
82 	device_t			dev;
83 
84 	struct clkdom			*clkdom;
85 };
86 
87 static int ti_clkctrl_probe(device_t dev);
88 static int ti_clkctrl_attach(device_t dev);
89 static int ti_clkctrl_detach(device_t dev);
90 int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
91     phandle_t *cells, struct clknode **clk);
92 static int
93 create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
94     uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg);
95 
96 static int
ti_clkctrl_probe(device_t dev)97 ti_clkctrl_probe(device_t dev)
98 {
99 	if (!ofw_bus_status_okay(dev))
100 		return (ENXIO);
101 
102 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
103 		return (ENXIO);
104 
105 	device_set_desc(dev, "TI clkctrl");
106 
107 	return (BUS_PROBE_DEFAULT);
108 }
109 
110 static int
ti_clkctrl_attach(device_t dev)111 ti_clkctrl_attach(device_t dev)
112 {
113 	struct ti_clkctrl_softc *sc;
114 	phandle_t node;
115 	cell_t	*reg;
116 	ssize_t numbytes_reg;
117 	int num_reg, err, ti_clock_cells;
118 	uint32_t index, reg_offset, reg_address;
119 	const char *org_name;
120 	uint64_t parent_offset;
121 	uint8_t special_reg = NO_SPECIAL_REG;
122 
123 	sc = device_get_softc(dev);
124 	sc->dev = dev;
125 	node = ofw_bus_get_node(dev);
126 
127 	/* Sanity check */
128 	err = OF_searchencprop(node, "#clock-cells",
129 		&ti_clock_cells, sizeof(ti_clock_cells));
130 	if (err == -1) {
131 		device_printf(sc->dev, "Failed to get #clock-cells\n");
132 		return (ENXIO);
133 	}
134 
135 	if (ti_clock_cells != 2) {
136 		device_printf(sc->dev, "clock cells(%d) != 2\n",
137 			ti_clock_cells);
138 		return (ENXIO);
139 	}
140 
141 	/* Grab the content of reg properties */
142 	numbytes_reg = OF_getproplen(node, "reg");
143 	if (numbytes_reg == 0) {
144 		device_printf(sc->dev, "reg property empty - check your devicetree\n");
145 		return (ENXIO);
146 	}
147 	num_reg = numbytes_reg / sizeof(cell_t);
148 
149 	reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK);
150 	OF_getencprop(node, "reg", reg, numbytes_reg);
151 
152 	/* Create clock domain */
153 	sc->clkdom = clkdom_create(sc->dev);
154 	if (sc->clkdom == NULL) {
155 		free(reg, M_DEVBUF);
156 		DPRINTF(sc->dev, "Failed to create clkdom\n");
157 		return (ENXIO);
158 	}
159 	clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map);
160 
161 	/* Create clock nodes */
162 	/* name */
163 	clk_parse_ofw_clk_name(sc->dev, node, &org_name);
164 
165 	/* Get parent range */
166 	parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev));
167 
168 	/* Check if this is a clkctrl with special registers like gpio */
169 	switch (ti_chip()) {
170 #ifdef SOC_OMAP4
171 	case CHIP_OMAP_4:
172 		/* FIXME: Todo */
173 		break;
174 
175 #endif /* SOC_OMAP4 */
176 #ifdef SOC_TI_AM335X
177 	/* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3
178 	 * and the DTS.
179 	 */
180 	case CHIP_AM335X:
181 		if (strcmp(org_name, "l4ls-clkctrl@38") == 0)
182 			special_reg = L4LS_CLKCTRL_38;
183 		else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0)
184 			special_reg = L4_WKUP_CLKCTRL_0;
185 		break;
186 #endif /* SOC_TI_AM335X */
187 	default:
188 		break;
189 	}
190 
191 	/* reg property has a pair of (base address, length) */
192 	for (index = 0; index < num_reg; index += 2) {
193 		for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) {
194 			err = create_clkctrl(sc, reg, index, reg_offset, parent_offset,
195 			    org_name, false);
196 			if (err)
197 				goto cleanup;
198 
199 			/* Create special clkctrl for GDBCLK in GPIO registers */
200 			switch (special_reg) {
201 			case NO_SPECIAL_REG:
202 				break;
203 			case L4LS_CLKCTRL_38:
204 				reg_address = reg[index] + reg_offset-reg[0];
205 				if (reg_address == 0x74 ||
206 				    reg_address == 0x78 ||
207 				    reg_address == 0x7C)
208 				{
209 					err = create_clkctrl(sc, reg, index, reg_offset,
210 					    parent_offset, org_name, true);
211 					if (err)
212 						goto cleanup;
213 				}
214 				break;
215 			case L4_WKUP_CLKCTRL_0:
216 				reg_address = reg[index] + reg_offset - reg[0];
217 				if (reg_address == 0x8)
218 				{
219 					err = create_clkctrl(sc, reg, index, reg_offset,
220 					    parent_offset, org_name, true);
221 					if (err)
222 						goto cleanup;
223 				}
224 				break;
225 			} /* switch (special_reg) */
226 		} /* inner for */
227 	} /* for */
228 
229 	err = clkdom_finit(sc->clkdom);
230 	if (err) {
231 		DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
232 		err = ENXIO;
233 		goto cleanup;
234 	}
235 
236 cleanup:
237 	OF_prop_free(__DECONST(char *, org_name));
238 
239 	free(reg, M_DEVBUF);
240 
241 	if (err)
242 		return (err);
243 
244 	return (bus_generic_attach(dev));
245 }
246 
247 static int
ti_clkctrl_detach(device_t dev)248 ti_clkctrl_detach(device_t dev)
249 {
250 	return (EBUSY);
251 }
252 
253 /* modified version of default mapper from clk.c */
254 int
clkctrl_ofw_map(struct clkdom * clkdom,uint32_t ncells,phandle_t * cells,struct clknode ** clk)255 clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
256     phandle_t *cells, struct clknode **clk) {
257 	if (ncells == 0)
258 		*clk = clknode_find_by_id(clkdom, 1);
259 	else if (ncells == 1)
260 		*clk = clknode_find_by_id(clkdom, cells[0]);
261 	else if (ncells == 2) {
262 		/* To avoid collision with other IDs just add one.
263 		 * All other registers has an offset of 4 from each other.
264 		 */
265 		if (cells[1])
266 			*clk = clknode_find_by_id(clkdom, cells[0]+1);
267 		else
268 			*clk = clknode_find_by_id(clkdom, cells[0]);
269 	}
270 	else
271 		return (ERANGE);
272 
273 	if (*clk == NULL)
274 		return (ENXIO);
275 
276 	return (0);
277 }
278 
279 static int
create_clkctrl(struct ti_clkctrl_softc * sc,cell_t * reg,uint32_t index,uint32_t reg_offset,uint64_t parent_offset,const char * org_name,bool special_gdbclk_reg)280 create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
281     uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) {
282 	struct ti_clk_clkctrl_def def;
283 	char *name;
284 	size_t name_len;
285 	int err;
286 
287 	name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */
288 	name = malloc(name_len, M_OFWPROP, M_WAITOK);
289 
290 	/*
291 	 * Check out XX_CLKCTRL-INDEX(offset)-macro dance in
292 	 * sys/gnu/dts/dts/include/dt-bindings/clock/am3.h
293 	 * sys/gnu/dts/dts/include/dt-bindings/clock/am4.h
294 	 * sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h
295 	 * reg[0] are in practice the same as the offset described in the dts.
296 	 */
297 	/* special_gdbclk_reg are 0 or 1 */
298 	def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg;
299 	def.register_offset = parent_offset + reg[index] + reg_offset;
300 
301 	/* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */
302 	def.gdbclk = special_gdbclk_reg;
303 
304 	/* Make up an uniq name in the namespace for each clkctrl */
305 	snprintf(name, name_len, "%s_%x",
306 		org_name, def.clkdef.id);
307 	def.clkdef.name = (const char *) name;
308 
309 	DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n",
310 		index, def.clkdef.name, def.clkdef.id);
311 
312 	/* No parent name */
313 	def.clkdef.parent_cnt = 0;
314 
315 	/* set flags */
316 	def.clkdef.flags = 0x0;
317 
318 	/* Register the clkctrl */
319 	err = ti_clknode_clkctrl_register(sc->clkdom, &def);
320 	if (err) {
321 		DPRINTF(sc->dev,
322 			"ti_clknode_clkctrl_register[%d:%d] failed %x\n",
323 			index, reg_offset, err);
324 		err = ENXIO;
325 	}
326 	OF_prop_free(name);
327 	return (err);
328 }
329 
330 static device_method_t ti_clkctrl_methods[] = {
331 	/* Device interface */
332 	DEVMETHOD(device_probe,		ti_clkctrl_probe),
333 	DEVMETHOD(device_attach,	ti_clkctrl_attach),
334 	DEVMETHOD(device_detach,	ti_clkctrl_detach),
335 
336 	DEVMETHOD_END
337 };
338 
339 DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods,
340     sizeof(struct ti_clkctrl_softc));
341 
342 EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver, 0, 0,
343     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
344 
345 MODULE_VERSION(ti_clkctrl, 1);
346