xref: /f-stack/freebsd/arm/allwinner/clkng/aw_ccung.c (revision 22ce4aff)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2017,2018 Emmanuel Vadot <[email protected]>
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  * $FreeBSD$
28  */
29 
30 /*
31  * Allwinner Clock Control Unit
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/bus.h>
40 #include <sys/rman.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/module.h>
44 #include <sys/mutex.h>
45 #include <machine/bus.h>
46 
47 #include <dev/fdt/simplebus.h>
48 
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51 
52 #include <dev/extres/clk/clk.h>
53 #include <dev/extres/clk/clk_gate.h>
54 
55 #include <dev/extres/hwreset/hwreset.h>
56 
57 #include <arm/allwinner/clkng/aw_ccung.h>
58 #include <arm/allwinner/clkng/aw_clk.h>
59 
60 #ifdef __aarch64__
61 #include "opt_soc.h"
62 #endif
63 
64 #include "clkdev_if.h"
65 #include "hwreset_if.h"
66 
67 #if 0
68 #define dprintf(format, arg...)	device_printf(dev, "%s: " format, __func__, arg)
69 #else
70 #define dprintf(format, arg...)
71 #endif
72 
73 static struct resource_spec aw_ccung_spec[] = {
74 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
75 	{ -1, 0 }
76 };
77 
78 #define	CCU_READ4(sc, reg)		bus_read_4((sc)->res, (reg))
79 #define	CCU_WRITE4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
80 
81 static int
aw_ccung_write_4(device_t dev,bus_addr_t addr,uint32_t val)82 aw_ccung_write_4(device_t dev, bus_addr_t addr, uint32_t val)
83 {
84 	struct aw_ccung_softc *sc;
85 
86 	sc = device_get_softc(dev);
87 	dprintf("offset=%lx write %x\n", addr, val);
88 	CCU_WRITE4(sc, addr, val);
89 	return (0);
90 }
91 
92 static int
aw_ccung_read_4(device_t dev,bus_addr_t addr,uint32_t * val)93 aw_ccung_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
94 {
95 	struct aw_ccung_softc *sc;
96 
97 	sc = device_get_softc(dev);
98 
99 	*val = CCU_READ4(sc, addr);
100 	dprintf("offset=%lx Read %x\n", addr, *val);
101 	return (0);
102 }
103 
104 static int
aw_ccung_modify_4(device_t dev,bus_addr_t addr,uint32_t clr,uint32_t set)105 aw_ccung_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
106 {
107 	struct aw_ccung_softc *sc;
108 	uint32_t reg;
109 
110 	sc = device_get_softc(dev);
111 
112 	dprintf("offset=%lx clr: %x set: %x\n", addr, clr, set);
113 	reg = CCU_READ4(sc, addr);
114 	reg &= ~clr;
115 	reg |= set;
116 	CCU_WRITE4(sc, addr, reg);
117 
118 	return (0);
119 }
120 
121 static int
aw_ccung_reset_assert(device_t dev,intptr_t id,bool reset)122 aw_ccung_reset_assert(device_t dev, intptr_t id, bool reset)
123 {
124 	struct aw_ccung_softc *sc;
125 	uint32_t val;
126 
127 	sc = device_get_softc(dev);
128 
129 	dprintf("%sassert reset id %ld\n", reset ? "" : "De", id);
130 	if (id >= sc->nresets || sc->resets[id].offset == 0)
131 		return (0);
132 
133 	mtx_lock(&sc->mtx);
134 	val = CCU_READ4(sc, sc->resets[id].offset);
135 	dprintf("offset=%x Read %x\n", sc->resets[id].offset, val);
136 	if (reset)
137 		val &= ~(1 << sc->resets[id].shift);
138 	else
139 		val |= 1 << sc->resets[id].shift;
140 	dprintf("offset=%x Write %x\n", sc->resets[id].offset, val);
141 	CCU_WRITE4(sc, sc->resets[id].offset, val);
142 	mtx_unlock(&sc->mtx);
143 
144 	return (0);
145 }
146 
147 static int
aw_ccung_reset_is_asserted(device_t dev,intptr_t id,bool * reset)148 aw_ccung_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
149 {
150 	struct aw_ccung_softc *sc;
151 	uint32_t val;
152 
153 	sc = device_get_softc(dev);
154 
155 	if (id >= sc->nresets || sc->resets[id].offset == 0)
156 		return (0);
157 
158 	mtx_lock(&sc->mtx);
159 	val = CCU_READ4(sc, sc->resets[id].offset);
160 	dprintf("offset=%x Read %x\n", sc->resets[id].offset, val);
161 	*reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true;
162 	mtx_unlock(&sc->mtx);
163 
164 	return (0);
165 }
166 
167 static void
aw_ccung_device_lock(device_t dev)168 aw_ccung_device_lock(device_t dev)
169 {
170 	struct aw_ccung_softc *sc;
171 
172 	sc = device_get_softc(dev);
173 	mtx_lock(&sc->mtx);
174 }
175 
176 static void
aw_ccung_device_unlock(device_t dev)177 aw_ccung_device_unlock(device_t dev)
178 {
179 	struct aw_ccung_softc *sc;
180 
181 	sc = device_get_softc(dev);
182 	mtx_unlock(&sc->mtx);
183 }
184 
185 static int
aw_ccung_register_gates(struct aw_ccung_softc * sc)186 aw_ccung_register_gates(struct aw_ccung_softc *sc)
187 {
188 	struct clk_gate_def def;
189 	int i;
190 
191 	for (i = 0; i < sc->ngates; i++) {
192 		if (sc->gates[i].name == NULL)
193 			continue;
194 		memset(&def, 0, sizeof(def));
195 		def.clkdef.id = i;
196 		def.clkdef.name = sc->gates[i].name;
197 		def.clkdef.parent_names = &sc->gates[i].parent_name;
198 		def.clkdef.parent_cnt = 1;
199 		def.offset = sc->gates[i].offset;
200 		def.shift = sc->gates[i].shift;
201 		def.mask = 1;
202 		def.on_value = 1;
203 		def.off_value = 0;
204 		clknode_gate_register(sc->clkdom, &def);
205 	}
206 
207 	return (0);
208 }
209 
210 static void
aw_ccung_init_clocks(struct aw_ccung_softc * sc)211 aw_ccung_init_clocks(struct aw_ccung_softc *sc)
212 {
213 	struct clknode *clknode;
214 	int i, error;
215 
216 	for (i = 0; i < sc->n_clk_init; i++) {
217 		clknode = clknode_find_by_name(sc->clk_init[i].name);
218 		if (clknode == NULL) {
219 			device_printf(sc->dev, "Cannot find clock %s\n",
220 			    sc->clk_init[i].name);
221 			continue;
222 		}
223 
224 		if (sc->clk_init[i].parent_name != NULL) {
225 			if (bootverbose)
226 				device_printf(sc->dev, "Setting %s as parent for %s\n",
227 				    sc->clk_init[i].parent_name,
228 				    sc->clk_init[i].name);
229 			error = clknode_set_parent_by_name(clknode,
230 			    sc->clk_init[i].parent_name);
231 			if (error != 0) {
232 				device_printf(sc->dev,
233 				    "Cannot set parent to %s for %s\n",
234 				    sc->clk_init[i].parent_name,
235 				    sc->clk_init[i].name);
236 				continue;
237 			}
238 		}
239 		if (sc->clk_init[i].default_freq != 0) {
240 			if (bootverbose)
241 				device_printf(sc->dev,
242 				    "Setting freq %ju for %s\n",
243 				    sc->clk_init[i].default_freq,
244 				    sc->clk_init[i].name);
245 			error = clknode_set_freq(clknode,
246 			    sc->clk_init[i].default_freq, 0 , 0);
247 			if (error != 0) {
248 				device_printf(sc->dev,
249 				    "Cannot set frequency for %s to %ju\n",
250 				    sc->clk_init[i].name,
251 				    sc->clk_init[i].default_freq);
252 				continue;
253 			}
254 		}
255 		if (sc->clk_init[i].enable) {
256 			error = clknode_enable(clknode);
257 			if (error != 0) {
258 				device_printf(sc->dev,
259 				    "Cannot enable %s\n",
260 				    sc->clk_init[i].name);
261 				continue;
262 			}
263 		}
264 	}
265 }
266 
267 int
aw_ccung_attach(device_t dev)268 aw_ccung_attach(device_t dev)
269 {
270 	struct aw_ccung_softc *sc;
271 	int i;
272 
273 	sc = device_get_softc(dev);
274 	sc->dev = dev;
275 
276 	if (bus_alloc_resources(dev, aw_ccung_spec, &sc->res) != 0) {
277 		device_printf(dev, "cannot allocate resources for device\n");
278 		return (ENXIO);
279 	}
280 
281 	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
282 
283 	sc->clkdom = clkdom_create(dev);
284 	if (sc->clkdom == NULL)
285 		panic("Cannot create clkdom\n");
286 
287 	for (i = 0; i < sc->nclks; i++) {
288 		switch (sc->clks[i].type) {
289 		case AW_CLK_UNDEFINED:
290 			break;
291 		case AW_CLK_MUX:
292 			clknode_mux_register(sc->clkdom, sc->clks[i].clk.mux);
293 			break;
294 		case AW_CLK_DIV:
295 			clknode_div_register(sc->clkdom, sc->clks[i].clk.div);
296 			break;
297 		case AW_CLK_FIXED:
298 			clknode_fixed_register(sc->clkdom,
299 			    sc->clks[i].clk.fixed);
300 			break;
301 		case AW_CLK_NKMP:
302 			aw_clk_nkmp_register(sc->clkdom, sc->clks[i].clk.nkmp);
303 			break;
304 		case AW_CLK_NM:
305 			aw_clk_nm_register(sc->clkdom, sc->clks[i].clk.nm);
306 			break;
307 		case AW_CLK_M:
308 			aw_clk_m_register(sc->clkdom, sc->clks[i].clk.m);
309 			break;
310 		case AW_CLK_PREDIV_MUX:
311 			aw_clk_prediv_mux_register(sc->clkdom,
312 			    sc->clks[i].clk.prediv_mux);
313 			break;
314 		case AW_CLK_FRAC:
315 			aw_clk_frac_register(sc->clkdom, sc->clks[i].clk.frac);
316 			break;
317 		case AW_CLK_MIPI:
318 			aw_clk_mipi_register(sc->clkdom, sc->clks[i].clk.mipi);
319 			break;
320 		case AW_CLK_NP:
321 			aw_clk_np_register(sc->clkdom, sc->clks[i].clk.np);
322 			break;
323 		case AW_CLK_NMM:
324 			aw_clk_nmm_register(sc->clkdom, sc->clks[i].clk.nmm);
325 			break;
326 		}
327 	}
328 
329 	if (sc->gates)
330 		aw_ccung_register_gates(sc);
331 	if (clkdom_finit(sc->clkdom) != 0)
332 		panic("cannot finalize clkdom initialization\n");
333 
334 	clkdom_xlock(sc->clkdom);
335 	aw_ccung_init_clocks(sc);
336 	clkdom_unlock(sc->clkdom);
337 
338 	if (bootverbose)
339 		clkdom_dump(sc->clkdom);
340 
341 	/* If we have resets, register our self as a reset provider */
342 	if (sc->resets)
343 		hwreset_register_ofw_provider(dev);
344 
345 	return (0);
346 }
347 
348 static device_method_t aw_ccung_methods[] = {
349 	/* clkdev interface */
350 	DEVMETHOD(clkdev_write_4,	aw_ccung_write_4),
351 	DEVMETHOD(clkdev_read_4,	aw_ccung_read_4),
352 	DEVMETHOD(clkdev_modify_4,	aw_ccung_modify_4),
353 	DEVMETHOD(clkdev_device_lock,	aw_ccung_device_lock),
354 	DEVMETHOD(clkdev_device_unlock,	aw_ccung_device_unlock),
355 
356 	/* Reset interface */
357 	DEVMETHOD(hwreset_assert,	aw_ccung_reset_assert),
358 	DEVMETHOD(hwreset_is_asserted,	aw_ccung_reset_is_asserted),
359 
360 	DEVMETHOD_END
361 };
362 
363 DEFINE_CLASS_0(aw_ccung, aw_ccung_driver, aw_ccung_methods,
364     sizeof(struct aw_ccung_softc));
365