1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * $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
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
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
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
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
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
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
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
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
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
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));
Cache object: 3eefda4512bb657fa4bef1afcd8e1294
|