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