]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/clkng/aw_ccung.c
Merge ^/vendor/llvm-project/master until just before r356843.
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / clkng / aw_ccung.c
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));