xref: /freebsd/sys/arm/ti/clk/ti_clkctrl.c (revision 10bb91672a02b646e6dad6cdfe10ff75a192db99)
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_TI_AM335X
171 	/* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3
172 	 * and the DTS.
173 	 */
174 	case CHIP_AM335X:
175 		if (strcmp(org_name, "l4ls-clkctrl@38") == 0)
176 			special_reg = L4LS_CLKCTRL_38;
177 		else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0)
178 			special_reg = L4_WKUP_CLKCTRL_0;
179 		break;
180 #endif /* SOC_TI_AM335X */
181 	default:
182 		break;
183 	}
184 
185 	/* reg property has a pair of (base address, length) */
186 	for (index = 0; index < num_reg; index += 2) {
187 		for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) {
188 			err = create_clkctrl(sc, reg, index, reg_offset, parent_offset,
189 			    org_name, false);
190 			if (err)
191 				goto cleanup;
192 
193 			/* Create special clkctrl for GDBCLK in GPIO registers */
194 			switch (special_reg) {
195 			case NO_SPECIAL_REG:
196 				break;
197 			case L4LS_CLKCTRL_38:
198 				reg_address = reg[index] + reg_offset-reg[0];
199 				if (reg_address == 0x74 ||
200 				    reg_address == 0x78 ||
201 				    reg_address == 0x7C)
202 				{
203 					err = create_clkctrl(sc, reg, index, reg_offset,
204 					    parent_offset, org_name, true);
205 					if (err)
206 						goto cleanup;
207 				}
208 				break;
209 			case L4_WKUP_CLKCTRL_0:
210 				reg_address = reg[index] + reg_offset - reg[0];
211 				if (reg_address == 0x8)
212 				{
213 					err = create_clkctrl(sc, reg, index, reg_offset,
214 					    parent_offset, org_name, true);
215 					if (err)
216 						goto cleanup;
217 				}
218 				break;
219 			} /* switch (special_reg) */
220 		} /* inner for */
221 	} /* for */
222 
223 	err = clkdom_finit(sc->clkdom);
224 	if (err) {
225 		DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err);
226 		err = ENXIO;
227 		goto cleanup;
228 	}
229 
230 cleanup:
231 	OF_prop_free(__DECONST(char *, org_name));
232 
233 	free(reg, M_DEVBUF);
234 
235 	if (err)
236 		return (err);
237 
238 	bus_attach_children(dev);
239 	return (0);
240 }
241 
242 static int
ti_clkctrl_detach(device_t dev)243 ti_clkctrl_detach(device_t dev)
244 {
245 	return (EBUSY);
246 }
247 
248 /* modified version of default mapper from clk.c */
249 int
clkctrl_ofw_map(struct clkdom * clkdom,uint32_t ncells,phandle_t * cells,struct clknode ** clk)250 clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells,
251     phandle_t *cells, struct clknode **clk) {
252 	if (ncells == 0)
253 		*clk = clknode_find_by_id(clkdom, 1);
254 	else if (ncells == 1)
255 		*clk = clknode_find_by_id(clkdom, cells[0]);
256 	else if (ncells == 2) {
257 		/* To avoid collision with other IDs just add one.
258 		 * All other registers has an offset of 4 from each other.
259 		 */
260 		if (cells[1])
261 			*clk = clknode_find_by_id(clkdom, cells[0]+1);
262 		else
263 			*clk = clknode_find_by_id(clkdom, cells[0]);
264 	}
265 	else
266 		return (ERANGE);
267 
268 	if (*clk == NULL)
269 		return (ENXIO);
270 
271 	return (0);
272 }
273 
274 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)275 create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset,
276     uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) {
277 	struct ti_clk_clkctrl_def def;
278 	char *name;
279 	size_t name_len;
280 	int err;
281 
282 	name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */
283 	name = malloc(name_len, M_OFWPROP, M_WAITOK);
284 
285 	/*
286 	 * Check out XX_CLKCTRL-INDEX(offset)-macro dance in
287 	 * sys/gnu/dts/dts/include/dt-bindings/clock/am3.h
288 	 * sys/gnu/dts/dts/include/dt-bindings/clock/am4.h
289 	 * sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h
290 	 * reg[0] are in practice the same as the offset described in the dts.
291 	 */
292 	/* special_gdbclk_reg are 0 or 1 */
293 	def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg;
294 	def.register_offset = parent_offset + reg[index] + reg_offset;
295 
296 	/* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */
297 	def.gdbclk = special_gdbclk_reg;
298 
299 	/* Make up an uniq name in the namespace for each clkctrl */
300 	snprintf(name, name_len, "%s_%x",
301 		org_name, def.clkdef.id);
302 	def.clkdef.name = (const char *) name;
303 
304 	DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n",
305 		index, def.clkdef.name, def.clkdef.id);
306 
307 	/* No parent name */
308 	def.clkdef.parent_cnt = 0;
309 
310 	/* set flags */
311 	def.clkdef.flags = 0x0;
312 
313 	/* Register the clkctrl */
314 	err = ti_clknode_clkctrl_register(sc->clkdom, &def);
315 	if (err) {
316 		DPRINTF(sc->dev,
317 			"ti_clknode_clkctrl_register[%d:%d] failed %x\n",
318 			index, reg_offset, err);
319 		err = ENXIO;
320 	}
321 	OF_prop_free(name);
322 	return (err);
323 }
324 
325 static device_method_t ti_clkctrl_methods[] = {
326 	/* Device interface */
327 	DEVMETHOD(device_probe,		ti_clkctrl_probe),
328 	DEVMETHOD(device_attach,	ti_clkctrl_attach),
329 	DEVMETHOD(device_detach,	ti_clkctrl_detach),
330 
331 	DEVMETHOD_END
332 };
333 
334 DEFINE_CLASS_0(ti_clkctrl, ti_clkctrl_driver, ti_clkctrl_methods,
335     sizeof(struct ti_clkctrl_softc));
336 
337 EARLY_DRIVER_MODULE(ti_clkctrl, simplebus, ti_clkctrl_driver, 0, 0,
338     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
339 
340 MODULE_VERSION(ti_clkctrl, 1);
341